cf-haproxy.c (6620B)
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 42 typedef 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 48 struct cf_haproxy_ctx { 49 int state; 50 struct dynbuf data_out; 51 }; 52 53 static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx) 54 { 55 DEBUGASSERT(ctx); 56 ctx->state = HAPROXY_INIT; 57 curlx_dyn_reset(&ctx->data_out); 58 } 59 60 static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx) 61 { 62 if(ctx) { 63 curlx_dyn_free(&ctx->data_out); 64 free(ctx); 65 } 66 } 67 68 static 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 *client_ip; 74 struct ip_quadruple ipquad; 75 int is_ipv6; 76 77 DEBUGASSERT(ctx); 78 DEBUGASSERT(ctx->state == HAPROXY_INIT); 79 #ifdef USE_UNIX_SOCKETS 80 if(cf->conn->unix_domain_socket) 81 /* the buffer is large enough to hold this! */ 82 result = curlx_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n")); 83 else { 84 #endif /* USE_UNIX_SOCKETS */ 85 result = Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad); 86 if(result) 87 return result; 88 89 /* Emit the correct prefix for IPv6 */ 90 if(data->set.str[STRING_HAPROXY_CLIENT_IP]) 91 client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; 92 else 93 client_ip = ipquad.local_ip; 94 95 result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", 96 is_ipv6 ? "TCP6" : "TCP4", 97 client_ip, ipquad.remote_ip, 98 ipquad.local_port, ipquad.remote_port); 99 100 #ifdef USE_UNIX_SOCKETS 101 } 102 #endif /* USE_UNIX_SOCKETS */ 103 return result; 104 } 105 106 static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf, 107 struct Curl_easy *data, 108 bool *done) 109 { 110 struct cf_haproxy_ctx *ctx = cf->ctx; 111 CURLcode result; 112 size_t len; 113 114 DEBUGASSERT(ctx); 115 if(cf->connected) { 116 *done = TRUE; 117 return CURLE_OK; 118 } 119 120 result = cf->next->cft->do_connect(cf->next, data, done); 121 if(result || !*done) 122 return result; 123 124 switch(ctx->state) { 125 case HAPROXY_INIT: 126 result = cf_haproxy_date_out_set(cf, data); 127 if(result) 128 goto out; 129 ctx->state = HAPROXY_SEND; 130 FALLTHROUGH(); 131 case HAPROXY_SEND: 132 len = curlx_dyn_len(&ctx->data_out); 133 if(len > 0) { 134 size_t nwritten; 135 result = Curl_conn_cf_send(cf->next, data, 136 curlx_dyn_ptr(&ctx->data_out), len, FALSE, 137 &nwritten); 138 if(result) { 139 if(result != CURLE_AGAIN) 140 goto out; 141 result = CURLE_OK; 142 nwritten = 0; 143 } 144 curlx_dyn_tail(&ctx->data_out, len - nwritten); 145 if(curlx_dyn_len(&ctx->data_out) > 0) { 146 result = CURLE_OK; 147 goto out; 148 } 149 } 150 ctx->state = HAPROXY_DONE; 151 FALLTHROUGH(); 152 default: 153 curlx_dyn_free(&ctx->data_out); 154 break; 155 } 156 157 out: 158 *done = (!result) && (ctx->state == HAPROXY_DONE); 159 cf->connected = *done; 160 return result; 161 } 162 163 static void cf_haproxy_destroy(struct Curl_cfilter *cf, 164 struct Curl_easy *data) 165 { 166 (void)data; 167 CURL_TRC_CF(data, cf, "destroy"); 168 cf_haproxy_ctx_free(cf->ctx); 169 } 170 171 static void cf_haproxy_close(struct Curl_cfilter *cf, 172 struct Curl_easy *data) 173 { 174 CURL_TRC_CF(data, cf, "close"); 175 cf->connected = FALSE; 176 cf_haproxy_ctx_reset(cf->ctx); 177 if(cf->next) 178 cf->next->cft->do_close(cf->next, data); 179 } 180 181 static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf, 182 struct Curl_easy *data, 183 struct easy_pollset *ps) 184 { 185 if(cf->next->connected && !cf->connected) { 186 /* If we are not connected, but the filter "below" is 187 * and not waiting on something, we are sending. */ 188 Curl_pollset_set_out_only(data, ps, Curl_conn_cf_get_socket(cf, data)); 189 } 190 } 191 192 struct Curl_cftype Curl_cft_haproxy = { 193 "HAPROXY", 194 CF_TYPE_PROXY, 195 0, 196 cf_haproxy_destroy, 197 cf_haproxy_connect, 198 cf_haproxy_close, 199 Curl_cf_def_shutdown, 200 cf_haproxy_adjust_pollset, 201 Curl_cf_def_data_pending, 202 Curl_cf_def_send, 203 Curl_cf_def_recv, 204 Curl_cf_def_cntrl, 205 Curl_cf_def_conn_is_alive, 206 Curl_cf_def_conn_keep_alive, 207 Curl_cf_def_query, 208 }; 209 210 static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf, 211 struct Curl_easy *data) 212 { 213 struct Curl_cfilter *cf = NULL; 214 struct cf_haproxy_ctx *ctx; 215 CURLcode result; 216 217 (void)data; 218 ctx = calloc(1, sizeof(*ctx)); 219 if(!ctx) { 220 result = CURLE_OUT_OF_MEMORY; 221 goto out; 222 } 223 ctx->state = HAPROXY_INIT; 224 curlx_dyn_init(&ctx->data_out, DYN_HAXPROXY); 225 226 result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx); 227 if(result) 228 goto out; 229 ctx = NULL; 230 231 out: 232 cf_haproxy_ctx_free(ctx); 233 *pcf = result ? NULL : cf; 234 return result; 235 } 236 237 CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at, 238 struct Curl_easy *data) 239 { 240 struct Curl_cfilter *cf; 241 CURLcode result; 242 243 result = cf_haproxy_create(&cf, data); 244 if(result) 245 goto out; 246 Curl_conn_cf_insert_after(cf_at, cf); 247 248 out: 249 return result; 250 } 251 252 #endif /* !CURL_DISABLE_PROXY */