Coverage Report

Created: 2026-05-30 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/cf-haproxy.c
Line
Count
Source
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
#include "curl_setup.h"
25
26
#ifndef CURL_DISABLE_PROXY
27
28
#include "urldata.h"
29
#include "cfilters.h"
30
#include "cf-haproxy.h"
31
#include "connect.h"
32
#include "curl_addrinfo.h"
33
#include "curl_trc.h"
34
#include "select.h"
35
36
37
typedef enum {
38
  HAPROXY_INIT,     /* init/default/no tunnel state */
39
  HAPROXY_SEND,     /* data_out being sent */
40
  HAPROXY_DONE      /* all work done */
41
} haproxy_state;
42
43
struct cf_haproxy_ctx {
44
  int state;
45
  struct dynbuf data_out;
46
};
47
48
static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx)
49
57
{
50
57
  DEBUGASSERT(ctx);
51
57
  ctx->state = HAPROXY_INIT;
52
57
  curlx_dyn_reset(&ctx->data_out);
53
57
}
54
55
static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
56
1.43k
{
57
1.43k
  if(ctx) {
58
717
    curlx_dyn_free(&ctx->data_out);
59
717
    curlx_free(ctx);
60
717
  }
61
1.43k
}
62
63
static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf,
64
                                        struct Curl_easy *data)
65
717
{
66
  /* We fake a client connection report to the upstream server
67
   * with the HAProxy protocol, reporting the client's source
68
   * and destination IP addresses and ports.
69
   * addresses: either the ones used to talk to the upstream
70
   *            OR the value supplied by the user
71
   * ports: the ports used in the upstream connection */
72
717
  const char *client_source_ip;
73
717
  const char *client_dest_ip;
74
717
  struct cf_haproxy_ctx *ctx = cf->ctx;
75
717
  CURLcode result;
76
717
  struct ip_quadruple ipquad;
77
717
  bool is_ipv6;
78
79
717
  DEBUGASSERT(ctx);
80
717
  DEBUGASSERT(ctx->state == HAPROXY_INIT);
81
717
#ifdef USE_UNIX_SOCKETS
82
717
  if(Curl_conn_get_first_peer(cf->conn, cf->sockindex)->unix_socket)
83
    /* the buffer is large enough to hold this! */
84
210
    result = curlx_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
85
507
  else {
86
507
#endif /* USE_UNIX_SOCKETS */
87
507
  result = Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad);
88
507
  if(result)
89
0
    return result;
90
91
507
  if(data->set.str[STRING_HAPROXY_CLIENT_IP]) {
92
417
    client_source_ip = data->set.str[STRING_HAPROXY_CLIENT_IP];
93
417
    client_dest_ip = client_source_ip;
94
417
    is_ipv6 = !Curl_is_ipv4addr(client_source_ip);
95
417
  }
96
90
  else {
97
90
    client_source_ip = ipquad.local_ip;
98
90
    client_dest_ip = ipquad.remote_ip;
99
90
  }
100
101
507
  result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
102
507
                          is_ipv6 ? "TCP6" : "TCP4",
103
507
                          client_source_ip, client_dest_ip,
104
507
                          ipquad.local_port, ipquad.remote_port);
105
106
507
#ifdef USE_UNIX_SOCKETS
107
507
  }
108
717
#endif /* USE_UNIX_SOCKETS */
109
717
  return result;
110
717
}
111
112
static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
113
                                   struct Curl_easy *data,
114
                                   bool *done)
115
717
{
116
717
  struct cf_haproxy_ctx *ctx = cf->ctx;
117
717
  CURLcode result;
118
717
  size_t len;
119
120
717
  DEBUGASSERT(ctx);
121
717
  if(cf->connected) {
122
0
    *done = TRUE;
123
0
    return CURLE_OK;
124
0
  }
125
126
717
  result = cf->next->cft->do_connect(cf->next, data, done);
127
717
  if(result || !*done)
128
0
    return result;
129
130
717
  switch(ctx->state) {
131
717
  case HAPROXY_INIT:
132
717
    result = cf_haproxy_date_out_set(cf, data);
133
717
    if(result)
134
20
      goto out;
135
697
    ctx->state = HAPROXY_SEND;
136
697
    FALLTHROUGH();
137
697
  case HAPROXY_SEND:
138
697
    len = curlx_dyn_len(&ctx->data_out);
139
697
    if(len > 0) {
140
697
      size_t nwritten;
141
697
      result = Curl_conn_cf_send(cf->next, data,
142
697
                                 curlx_dyn_uptr(&ctx->data_out), len, FALSE,
143
697
                                 &nwritten);
144
697
      if(result) {
145
0
        if(result != CURLE_AGAIN)
146
0
          goto out;
147
0
        result = CURLE_OK;
148
0
        nwritten = 0;
149
0
      }
150
697
      curlx_dyn_tail(&ctx->data_out, len - nwritten);
151
697
      if(curlx_dyn_len(&ctx->data_out) > 0) {
152
0
        result = CURLE_OK;
153
0
        goto out;
154
0
      }
155
697
    }
156
697
    ctx->state = HAPROXY_DONE;
157
697
    FALLTHROUGH();
158
697
  default:
159
697
    curlx_dyn_free(&ctx->data_out);
160
697
    break;
161
717
  }
162
163
717
out:
164
717
  *done = (!result) && (ctx->state == HAPROXY_DONE);
165
717
  cf->connected = *done;
166
717
  return result;
167
717
}
168
169
static void cf_haproxy_destroy(struct Curl_cfilter *cf,
170
                               struct Curl_easy *data)
171
717
{
172
717
  CURL_TRC_CF(data, cf, "destroy");
173
717
  cf_haproxy_ctx_free(cf->ctx);
174
717
}
175
176
static void cf_haproxy_close(struct Curl_cfilter *cf,
177
                             struct Curl_easy *data)
178
57
{
179
57
  CURL_TRC_CF(data, cf, "close");
180
57
  cf->connected = FALSE;
181
57
  cf_haproxy_ctx_reset(cf->ctx);
182
57
  if(cf->next)
183
57
    cf->next->cft->do_close(cf->next, data);
184
57
}
185
186
static CURLcode cf_haproxy_adjust_pollset(struct Curl_cfilter *cf,
187
                                          struct Curl_easy *data,
188
                                          struct easy_pollset *ps)
189
9
{
190
9
  if(cf->next->connected && !cf->connected) {
191
    /* If we are not connected, but the filter "below" is
192
     * and not waiting on something, we are sending. */
193
0
    return Curl_pollset_set_out_only(
194
0
      data, ps, Curl_conn_cf_get_socket(cf, data));
195
0
  }
196
9
  return CURLE_OK;
197
9
}
198
199
struct Curl_cftype Curl_cft_haproxy = {
200
  "HAPROXY",
201
  CF_TYPE_PROXY | CF_TYPE_SETUP,
202
  0,
203
  cf_haproxy_destroy,
204
  cf_haproxy_connect,
205
  cf_haproxy_close,
206
  Curl_cf_def_shutdown,
207
  cf_haproxy_adjust_pollset,
208
  Curl_cf_def_data_pending,
209
  Curl_cf_def_send,
210
  Curl_cf_def_recv,
211
  Curl_cf_def_cntrl,
212
  Curl_cf_def_conn_is_alive,
213
  Curl_cf_def_conn_keep_alive,
214
  Curl_cf_def_query,
215
};
216
217
static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
218
                                  struct Curl_easy *data)
219
717
{
220
717
  struct Curl_cfilter *cf = NULL;
221
717
  struct cf_haproxy_ctx *ctx;
222
717
  CURLcode result;
223
224
717
  (void)data;
225
717
  ctx = curlx_calloc(1, sizeof(*ctx));
226
717
  if(!ctx) {
227
0
    result = CURLE_OUT_OF_MEMORY;
228
0
    goto out;
229
0
  }
230
717
  ctx->state = HAPROXY_INIT;
231
717
  curlx_dyn_init(&ctx->data_out, DYN_HAXPROXY);
232
233
717
  result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx);
234
717
  if(result)
235
0
    goto out;
236
717
  ctx = NULL;
237
238
717
out:
239
717
  cf_haproxy_ctx_free(ctx);
240
717
  *pcf = result ? NULL : cf;
241
717
  return result;
242
717
}
243
244
CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
245
                                      struct Curl_easy *data)
246
717
{
247
717
  struct Curl_cfilter *cf;
248
717
  CURLcode result;
249
250
717
  result = cf_haproxy_create(&cf, data);
251
717
  if(result)
252
0
    goto out;
253
717
  Curl_conn_cf_insert_after(cf_at, cf);
254
255
717
out:
256
717
  return result;
257
717
}
258
259
#endif /* !CURL_DISABLE_PROXY */