Coverage Report

Created: 2023-06-07 07:02

/src/curl/lib/cf-haproxy.c
Line
Count
Source (jump to first uncovered line)
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_log.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
53
static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx)
54
0
{
55
0
  DEBUGASSERT(ctx);
56
0
  ctx->state = HAPROXY_INIT;
57
0
  Curl_dyn_reset(&ctx->data_out);
58
0
}
59
60
static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
61
0
{
62
0
  if(ctx) {
63
0
    Curl_dyn_free(&ctx->data_out);
64
0
    free(ctx);
65
0
  }
66
0
}
67
68
static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
69
                                        struct Curl_easy *data)
70
0
{
71
0
  struct cf_haproxy_ctx *ctx = cf->ctx;
72
0
  CURLcode result;
73
0
  const char *tcp_version;
74
0
  const char *client_ip;
75
76
0
  DEBUGASSERT(ctx);
77
0
  DEBUGASSERT(ctx->state == HAPROXY_INIT);
78
0
#ifdef USE_UNIX_SOCKETS
79
0
  if(cf->conn->unix_domain_socket)
80
    /* the buffer is large enough to hold this! */
81
0
    result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
82
0
  else {
83
0
#endif /* USE_UNIX_SOCKETS */
84
  /* Emit the correct prefix for IPv6 */
85
0
  tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4";
86
0
  if(data->set.str[STRING_HAPROXY_CLIENT_IP])
87
0
    client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP];
88
0
  else
89
0
    client_ip = data->info.conn_primary_ip;
90
91
0
  result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
92
0
                         tcp_version,
93
0
                         data->info.conn_local_ip,
94
0
                         client_ip,
95
0
                         data->info.conn_local_port,
96
0
                         data->info.conn_primary_port);
97
98
0
#ifdef USE_UNIX_SOCKETS
99
0
  }
100
0
#endif /* USE_UNIX_SOCKETS */
101
0
  return result;
102
0
}
103
104
static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
105
                                   struct Curl_easy *data,
106
                                   bool blocking, bool *done)
107
0
{
108
0
  struct cf_haproxy_ctx *ctx = cf->ctx;
109
0
  CURLcode result;
110
0
  size_t len;
111
112
0
  DEBUGASSERT(ctx);
113
0
  if(cf->connected) {
114
0
    *done = TRUE;
115
0
    return CURLE_OK;
116
0
  }
117
118
0
  result = cf->next->cft->connect(cf->next, data, blocking, done);
119
0
  if(result || !*done)
120
0
    return result;
121
122
0
  switch(ctx->state) {
123
0
  case HAPROXY_INIT:
124
0
    result = cf_haproxy_date_out_set(cf, data);
125
0
    if(result)
126
0
      goto out;
127
0
    ctx->state = HAPROXY_SEND;
128
    /* FALLTHROUGH */
129
0
  case HAPROXY_SEND:
130
0
    len = Curl_dyn_len(&ctx->data_out);
131
0
    if(len > 0) {
132
0
      ssize_t written = Curl_conn_send(data, cf->sockindex,
133
0
                                       Curl_dyn_ptr(&ctx->data_out),
134
0
                                       len, &result);
135
0
      if(written < 0)
136
0
        goto out;
137
0
      Curl_dyn_tail(&ctx->data_out, len - (size_t)written);
138
0
      if(Curl_dyn_len(&ctx->data_out) > 0) {
139
0
        result = CURLE_OK;
140
0
        goto out;
141
0
      }
142
0
    }
143
0
    ctx->state = HAPROXY_DONE;
144
    /* FALLTHROUGH */
145
0
  default:
146
0
    Curl_dyn_free(&ctx->data_out);
147
0
    break;
148
0
  }
149
150
0
out:
151
0
  *done = (!result) && (ctx->state == HAPROXY_DONE);
152
0
  cf->connected = *done;
153
0
  return result;
154
0
}
155
156
static void cf_haproxy_destroy(struct Curl_cfilter *cf,
157
                               struct Curl_easy *data)
158
0
{
159
0
  (void)data;
160
0
  DEBUGF(LOG_CF(data, cf, "destroy"));
161
0
  cf_haproxy_ctx_free(cf->ctx);
162
0
}
163
164
static void cf_haproxy_close(struct Curl_cfilter *cf,
165
                             struct Curl_easy *data)
166
0
{
167
0
  DEBUGF(LOG_CF(data, cf, "close"));
168
0
  cf->connected = FALSE;
169
0
  cf_haproxy_ctx_reset(cf->ctx);
170
0
  if(cf->next)
171
0
    cf->next->cft->close(cf->next, data);
172
0
}
173
174
static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf,
175
                                       struct Curl_easy *data,
176
                                       curl_socket_t *socks)
177
0
{
178
0
  int fds;
179
180
0
  fds = cf->next->cft->get_select_socks(cf->next, data, socks);
181
0
  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
0
    socks[0] = Curl_conn_cf_get_socket(cf, data);
185
0
    return GETSOCK_WRITESOCK(0);
186
0
  }
187
0
  return fds;
188
0
}
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
209
static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
210
                                  struct Curl_easy *data)
211
0
{
212
0
  struct Curl_cfilter *cf = NULL;
213
0
  struct cf_haproxy_ctx *ctx;
214
0
  CURLcode result;
215
216
0
  (void)data;
217
0
  ctx = calloc(sizeof(*ctx), 1);
218
0
  if(!ctx) {
219
0
    result = CURLE_OUT_OF_MEMORY;
220
0
    goto out;
221
0
  }
222
0
  ctx->state = HAPROXY_INIT;
223
0
  Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY);
224
225
0
  result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx);
226
0
  if(result)
227
0
    goto out;
228
0
  ctx = NULL;
229
230
0
out:
231
0
  cf_haproxy_ctx_free(ctx);
232
0
  *pcf = result? NULL : cf;
233
0
  return result;
234
0
}
235
236
CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
237
                                      struct Curl_easy *data)
238
0
{
239
0
  struct Curl_cfilter *cf;
240
0
  CURLcode result;
241
242
0
  result = cf_haproxy_create(&cf, data);
243
0
  if(result)
244
0
    goto out;
245
0
  Curl_conn_cf_insert_after(cf_at, cf);
246
247
0
out:
248
0
  return result;
249
0
}
250
251
#endif /* !CURL_DISABLE_PROXY */