xref: /third_party/curl/lib/cf-haproxy.c (revision 13498266)
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
42typedef 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
48struct cf_haproxy_ctx {
49  int state;
50  struct dynbuf data_out;
51};
52
53static 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
60static 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
68static 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
104static 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
150out:
151  *done = (!result) && (ctx->state == HAPROXY_DONE);
152  cf->connected = *done;
153  return result;
154}
155
156static 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
164static 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
174static 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
185struct 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
203static 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
224out:
225  cf_haproxy_ctx_free(ctx);
226  *pcf = result? NULL : cf;
227  return result;
228}
229
230CURLcode 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
241out:
242  return result;
243}
244
245#endif /* !CURL_DISABLE_PROXY */
246