#include "curl_setup.h"
#if !defined(CURL_DISABLE_PROXY)
#include <curl/curl.h>
#include "urldata.h"
#include "cfilters.h"
#include "cf-haproxy.h"
#include "curl_trc.h"
#include "multiif.h"
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
typedef enum {
HAPROXY_INIT,
HAPROXY_SEND,
HAPROXY_DONE
} haproxy_state;
struct cf_haproxy_ctx {
int state;
struct dynbuf data_out;
};
static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx)
{
DEBUGASSERT(ctx);
ctx->state = HAPROXY_INIT;
curlx_dyn_reset(&ctx->data_out);
}
static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
{
if(ctx) {
curlx_dyn_free(&ctx->data_out);
free(ctx);
}
}
static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
struct Curl_easy *data)
{
struct cf_haproxy_ctx *ctx = cf->ctx;
CURLcode result;
const char *client_ip;
struct ip_quadruple ipquad;
int is_ipv6;
DEBUGASSERT(ctx);
DEBUGASSERT(ctx->state == HAPROXY_INIT);
#ifdef USE_UNIX_SOCKETS
if(cf->conn->unix_domain_socket)
result = curlx_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
else {
#endif
result = Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad);
if(result)
return result;
if(data->set.str[STRING_HAPROXY_CLIENT_IP])
client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP];
else
client_ip = ipquad.local_ip;
result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
is_ipv6 ? "TCP6" : "TCP4",
client_ip, ipquad.remote_ip,
ipquad.local_port, ipquad.remote_port);
#ifdef USE_UNIX_SOCKETS
}
#endif
return result;
}
static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct cf_haproxy_ctx *ctx = cf->ctx;
CURLcode result;
size_t len;
DEBUGASSERT(ctx);
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
result = cf->next->cft->do_connect(cf->next, data, done);
if(result || !*done)
return result;
switch(ctx->state) {
case HAPROXY_INIT:
result = cf_haproxy_date_out_set(cf, data);
if(result)
goto out;
ctx->state = HAPROXY_SEND;
FALLTHROUGH();
case HAPROXY_SEND:
len = curlx_dyn_len(&ctx->data_out);
if(len > 0) {
ssize_t nwritten;
nwritten = Curl_conn_cf_send(cf->next, data,
curlx_dyn_ptr(&ctx->data_out), len, FALSE,
&result);
if(nwritten < 0) {
if(result != CURLE_AGAIN)
goto out;
result = CURLE_OK;
nwritten = 0;
}
curlx_dyn_tail(&ctx->data_out, len - (size_t)nwritten);
if(curlx_dyn_len(&ctx->data_out) > 0) {
result = CURLE_OK;
goto out;
}
}
ctx->state = HAPROXY_DONE;
FALLTHROUGH();
default:
curlx_dyn_free(&ctx->data_out);
break;
}
out:
*done = (!result) && (ctx->state == HAPROXY_DONE);
cf->connected = *done;
return result;
}
static void cf_haproxy_destroy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
(void)data;
CURL_TRC_CF(data, cf, "destroy");
cf_haproxy_ctx_free(cf->ctx);
}
static void cf_haproxy_close(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
CURL_TRC_CF(data, cf, "close");
cf->connected = FALSE;
cf_haproxy_ctx_reset(cf->ctx);
if(cf->next)
cf->next->cft->do_close(cf->next, data);
}
static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps)
{
if(cf->next->connected && !cf->connected) {
Curl_pollset_set_out_only(data, ps, Curl_conn_cf_get_socket(cf, data));
}
}
struct Curl_cftype Curl_cft_haproxy = {
"HAPROXY",
CF_TYPE_PROXY,
0,
cf_haproxy_destroy,
cf_haproxy_connect,
cf_haproxy_close,
Curl_cf_def_shutdown,
Curl_cf_def_get_host,
cf_haproxy_adjust_pollset,
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
Curl_cf_def_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
};
static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
struct Curl_easy *data)
{
struct Curl_cfilter *cf = NULL;
struct cf_haproxy_ctx *ctx;
CURLcode result;
(void)data;
ctx = calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
ctx->state = HAPROXY_INIT;
curlx_dyn_init(&ctx->data_out, DYN_HAXPROXY);
result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx);
if(result)
goto out;
ctx = NULL;
out:
cf_haproxy_ctx_free(ctx);
*pcf = result ? NULL : cf;
return result;
}
CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data)
{
struct Curl_cfilter *cf;
CURLcode result;
result = cf_haproxy_create(&cf, data);
if(result)
goto out;
Curl_conn_cf_insert_after(cf_at, cf);
out:
return result;
}
#endif