1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25#include "curl_setup.h" 26 27#if !defined(CURL_DISABLE_PROXY) 28 29#include <curl/curl.h> 30#include "urldata.h" 31#include "cfilters.h" 32#include "cf-haproxy.h" 33#include "curl_trc.h" 34#include "multiif.h" 35 36/* The last 3 #include files should be in this order */ 37#include "curl_printf.h" 38#include "curl_memory.h" 39#include "memdebug.h" 40 41 42typedef enum { 43 HAPROXY_INIT, /* init/default/no tunnel state */ 44 HAPROXY_SEND, /* data_out being sent */ 45 HAPROXY_DONE /* all work done */ 46} haproxy_state; 47 48struct cf_haproxy_ctx { 49 int state; 50 struct dynbuf data_out; 51}; 52 53static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx) 54{ 55 DEBUGASSERT(ctx); 56 ctx->state = HAPROXY_INIT; 57 Curl_dyn_reset(&ctx->data_out); 58} 59 60static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx) 61{ 62 if(ctx) { 63 Curl_dyn_free(&ctx->data_out); 64 free(ctx); 65 } 66} 67 68static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf, 69 struct Curl_easy *data) 70{ 71 struct cf_haproxy_ctx *ctx = cf->ctx; 72 CURLcode result; 73 const char *tcp_version; 74 const char *client_ip; 75 76 DEBUGASSERT(ctx); 77 DEBUGASSERT(ctx->state == HAPROXY_INIT); 78#ifdef USE_UNIX_SOCKETS 79 if(cf->conn->unix_domain_socket) 80 /* the buffer is large enough to hold this! */ 81 result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n")); 82 else { 83#endif /* USE_UNIX_SOCKETS */ 84 /* Emit the correct prefix for IPv6 */ 85 tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4"; 86 if(data->set.str[STRING_HAPROXY_CLIENT_IP]) 87 client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; 88 else 89 client_ip = data->info.conn_local_ip; 90 91 result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", 92 tcp_version, 93 client_ip, 94 data->info.conn_primary_ip, 95 data->info.conn_local_port, 96 data->info.conn_primary_port); 97 98#ifdef USE_UNIX_SOCKETS 99 } 100#endif /* USE_UNIX_SOCKETS */ 101 return result; 102} 103 104static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf, 105 struct Curl_easy *data, 106 bool blocking, bool *done) 107{ 108 struct cf_haproxy_ctx *ctx = cf->ctx; 109 CURLcode result; 110 size_t len; 111 112 DEBUGASSERT(ctx); 113 if(cf->connected) { 114 *done = TRUE; 115 return CURLE_OK; 116 } 117 118 result = cf->next->cft->do_connect(cf->next, data, blocking, done); 119 if(result || !*done) 120 return result; 121 122 switch(ctx->state) { 123 case HAPROXY_INIT: 124 result = cf_haproxy_date_out_set(cf, data); 125 if(result) 126 goto out; 127 ctx->state = HAPROXY_SEND; 128 FALLTHROUGH(); 129 case HAPROXY_SEND: 130 len = Curl_dyn_len(&ctx->data_out); 131 if(len > 0) { 132 ssize_t written = Curl_conn_send(data, cf->sockindex, 133 Curl_dyn_ptr(&ctx->data_out), 134 len, &result); 135 if(written < 0) 136 goto out; 137 Curl_dyn_tail(&ctx->data_out, len - (size_t)written); 138 if(Curl_dyn_len(&ctx->data_out) > 0) { 139 result = CURLE_OK; 140 goto out; 141 } 142 } 143 ctx->state = HAPROXY_DONE; 144 FALLTHROUGH(); 145 default: 146 Curl_dyn_free(&ctx->data_out); 147 break; 148 } 149 150out: 151 *done = (!result) && (ctx->state == HAPROXY_DONE); 152 cf->connected = *done; 153 return result; 154} 155 156static void cf_haproxy_destroy(struct Curl_cfilter *cf, 157 struct Curl_easy *data) 158{ 159 (void)data; 160 CURL_TRC_CF(data, cf, "destroy"); 161 cf_haproxy_ctx_free(cf->ctx); 162} 163 164static void cf_haproxy_close(struct Curl_cfilter *cf, 165 struct Curl_easy *data) 166{ 167 CURL_TRC_CF(data, cf, "close"); 168 cf->connected = FALSE; 169 cf_haproxy_ctx_reset(cf->ctx); 170 if(cf->next) 171 cf->next->cft->do_close(cf->next, data); 172} 173 174static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf, 175 struct Curl_easy *data, 176 struct easy_pollset *ps) 177{ 178 if(cf->next->connected && !cf->connected) { 179 /* If we are not connected, but the filter "below" is 180 * and not waiting on something, we are sending. */ 181 Curl_pollset_set_out_only(data, ps, Curl_conn_cf_get_socket(cf, data)); 182 } 183} 184 185struct Curl_cftype Curl_cft_haproxy = { 186 "HAPROXY", 187 0, 188 0, 189 cf_haproxy_destroy, 190 cf_haproxy_connect, 191 cf_haproxy_close, 192 Curl_cf_def_get_host, 193 cf_haproxy_adjust_pollset, 194 Curl_cf_def_data_pending, 195 Curl_cf_def_send, 196 Curl_cf_def_recv, 197 Curl_cf_def_cntrl, 198 Curl_cf_def_conn_is_alive, 199 Curl_cf_def_conn_keep_alive, 200 Curl_cf_def_query, 201}; 202 203static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf, 204 struct Curl_easy *data) 205{ 206 struct Curl_cfilter *cf = NULL; 207 struct cf_haproxy_ctx *ctx; 208 CURLcode result; 209 210 (void)data; 211 ctx = calloc(1, sizeof(*ctx)); 212 if(!ctx) { 213 result = CURLE_OUT_OF_MEMORY; 214 goto out; 215 } 216 ctx->state = HAPROXY_INIT; 217 Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY); 218 219 result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx); 220 if(result) 221 goto out; 222 ctx = NULL; 223 224out: 225 cf_haproxy_ctx_free(ctx); 226 *pcf = result? NULL : cf; 227 return result; 228} 229 230CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at, 231 struct Curl_easy *data) 232{ 233 struct Curl_cfilter *cf; 234 CURLcode result; 235 236 result = cf_haproxy_create(&cf, data); 237 if(result) 238 goto out; 239 Curl_conn_cf_insert_after(cf_at, cf); 240 241out: 242 return result; 243} 244 245#endif /* !CURL_DISABLE_PROXY */ 246