Coverage Report

Created: 2026-09-04 07:16

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