xref: /curl/lib/cf-haproxy.c (revision 32101010)
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.primary.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.primary.remote_ip,
95                          data->info.primary.local_port,
96                          data->info.primary.remote_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       size_t written;
133       result = Curl_conn_send(data, cf->sockindex,
134                               Curl_dyn_ptr(&ctx->data_out),
135                               len, &written);
136       if(result == CURLE_AGAIN) {
137         result = CURLE_OK;
138         written = 0;
139       }
140       else if(result)
141         goto out;
142       Curl_dyn_tail(&ctx->data_out, len - written);
143       if(Curl_dyn_len(&ctx->data_out) > 0) {
144         result = CURLE_OK;
145         goto out;
146       }
147     }
148     ctx->state = HAPROXY_DONE;
149     FALLTHROUGH();
150   default:
151     Curl_dyn_free(&ctx->data_out);
152     break;
153   }
154 
155 out:
156   *done = (!result) && (ctx->state == HAPROXY_DONE);
157   cf->connected = *done;
158   return result;
159 }
160 
cf_haproxy_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)161 static void cf_haproxy_destroy(struct Curl_cfilter *cf,
162                                struct Curl_easy *data)
163 {
164   (void)data;
165   CURL_TRC_CF(data, cf, "destroy");
166   cf_haproxy_ctx_free(cf->ctx);
167 }
168 
cf_haproxy_close(struct Curl_cfilter * cf,struct Curl_easy * data)169 static void cf_haproxy_close(struct Curl_cfilter *cf,
170                              struct Curl_easy *data)
171 {
172   CURL_TRC_CF(data, cf, "close");
173   cf->connected = FALSE;
174   cf_haproxy_ctx_reset(cf->ctx);
175   if(cf->next)
176     cf->next->cft->do_close(cf->next, data);
177 }
178 
cf_haproxy_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)179 static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf,
180                                       struct Curl_easy *data,
181                                       struct easy_pollset *ps)
182 {
183   if(cf->next->connected && !cf->connected) {
184     /* If we are not connected, but the filter "below" is
185      * and not waiting on something, we are sending. */
186     Curl_pollset_set_out_only(data, ps, Curl_conn_cf_get_socket(cf, data));
187   }
188 }
189 
190 struct Curl_cftype Curl_cft_haproxy = {
191   "HAPROXY",
192   CF_TYPE_PROXY,
193   0,
194   cf_haproxy_destroy,
195   cf_haproxy_connect,
196   cf_haproxy_close,
197   Curl_cf_def_get_host,
198   cf_haproxy_adjust_pollset,
199   Curl_cf_def_data_pending,
200   Curl_cf_def_send,
201   Curl_cf_def_recv,
202   Curl_cf_def_cntrl,
203   Curl_cf_def_conn_is_alive,
204   Curl_cf_def_conn_keep_alive,
205   Curl_cf_def_query,
206 };
207 
cf_haproxy_create(struct Curl_cfilter ** pcf,struct Curl_easy * data)208 static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
209                                   struct Curl_easy *data)
210 {
211   struct Curl_cfilter *cf = NULL;
212   struct cf_haproxy_ctx *ctx;
213   CURLcode result;
214 
215   (void)data;
216   ctx = calloc(1, sizeof(*ctx));
217   if(!ctx) {
218     result = CURLE_OUT_OF_MEMORY;
219     goto out;
220   }
221   ctx->state = HAPROXY_INIT;
222   Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY);
223 
224   result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx);
225   if(result)
226     goto out;
227   ctx = NULL;
228 
229 out:
230   cf_haproxy_ctx_free(ctx);
231   *pcf = result? NULL : cf;
232   return result;
233 }
234 
Curl_cf_haproxy_insert_after(struct Curl_cfilter * cf_at,struct Curl_easy * data)235 CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
236                                       struct Curl_easy *data)
237 {
238   struct Curl_cfilter *cf;
239   CURLcode result;
240 
241   result = cf_haproxy_create(&cf, data);
242   if(result)
243     goto out;
244   Curl_conn_cf_insert_after(cf_at, cf);
245 
246 out:
247   return result;
248 }
249 
250 #endif /* !CURL_DISABLE_PROXY */
251