• 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_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