Coverage Report

Created: 2026-06-15 07:03

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