• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_get_select_socks(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * socks)174 static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf,
175                                        struct Curl_easy *data,
176                                        curl_socket_t *socks)
177 {
178   int fds;
179 
180   fds = cf->next->cft->get_select_socks(cf->next, data, socks);
181   if(!fds && cf->next->connected && !cf->connected) {
182     /* If we are not connected, but the filter "below" is
183      * and not waiting on something, we are sending. */
184     socks[0] = Curl_conn_cf_get_socket(cf, data);
185     return GETSOCK_WRITESOCK(0);
186   }
187   return fds;
188 }
189 
190 
191 struct Curl_cftype Curl_cft_haproxy = {
192   "HAPROXY",
193   0,
194   0,
195   cf_haproxy_destroy,
196   cf_haproxy_connect,
197   cf_haproxy_close,
198   Curl_cf_def_get_host,
199   cf_haproxy_get_select_socks,
200   Curl_cf_def_data_pending,
201   Curl_cf_def_send,
202   Curl_cf_def_recv,
203   Curl_cf_def_cntrl,
204   Curl_cf_def_conn_is_alive,
205   Curl_cf_def_conn_keep_alive,
206   Curl_cf_def_query,
207 };
208 
cf_haproxy_create(struct Curl_cfilter ** pcf,struct Curl_easy * data)209 static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
210                                   struct Curl_easy *data)
211 {
212   struct Curl_cfilter *cf = NULL;
213   struct cf_haproxy_ctx *ctx;
214   CURLcode result;
215 
216   (void)data;
217   ctx = calloc(sizeof(*ctx), 1);
218   if(!ctx) {
219     result = CURLE_OUT_OF_MEMORY;
220     goto out;
221   }
222   ctx->state = HAPROXY_INIT;
223   Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY);
224 
225   result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx);
226   if(result)
227     goto out;
228   ctx = NULL;
229 
230 out:
231   cf_haproxy_ctx_free(ctx);
232   *pcf = result? NULL : cf;
233   return result;
234 }
235 
Curl_cf_haproxy_insert_after(struct Curl_cfilter * cf_at,struct Curl_easy * data)236 CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
237                                       struct Curl_easy *data)
238 {
239   struct Curl_cfilter *cf;
240   CURLcode result;
241 
242   result = cf_haproxy_create(&cf, data);
243   if(result)
244     goto out;
245   Curl_conn_cf_insert_after(cf_at, cf);
246 
247 out:
248   return result;
249 }
250 
251 #endif /* !CURL_DISABLE_PROXY */
252