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
cf_haproxy_ctx_reset(struct cf_haproxy_ctx * ctx)53 static 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
cf_haproxy_ctx_free(struct cf_haproxy_ctx * ctx)60 static 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
cf_haproxy_date_out_set(struct Curl_cfilter * cf,struct Curl_easy * data)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 *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
cf_haproxy_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)104 static 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
150 out:
151 *done = (!result) && (ctx->state == HAPROXY_DONE);
152 cf->connected = *done;
153 return result;
154 }
155
cf_haproxy_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)156 static 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
cf_haproxy_close(struct Curl_cfilter * cf,struct Curl_easy * data)164 static 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
cf_haproxy_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)174 static 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
185 struct 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
cf_haproxy_create(struct Curl_cfilter ** pcf,struct Curl_easy * data)203 static 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
224 out:
225 cf_haproxy_ctx_free(ctx);
226 *pcf = result? NULL : cf;
227 return result;
228 }
229
Curl_cf_haproxy_insert_after(struct Curl_cfilter * cf_at,struct Curl_easy * data)230 CURLcode 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
241 out:
242 return result;
243 }
244
245 #endif /* !CURL_DISABLE_PROXY */
246