pissircd/src/modules/webserver.c

873 lines
22 KiB
C

/*
* Webserver
* (C)Copyright 2016 Bram Matthys and the UnrealIRCd team
* License: GPLv2 or later
*/
#include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
"webserver",
"1.0.0",
"Webserver",
"UnrealIRCd Team",
"unrealircd-6",
};
#if CHAR_MIN < 0
#error "In UnrealIRCd char should always be unsigned. Check your compiler"
#endif
/* How many seconds to wait with closing after sending the response */
#define WEB_CLOSE_TIME 1
/* The "Server: xyz" in the response */
#define WEB_SOFTWARE "UnrealIRCd"
/* Macros */
#define WEB(client) ((WebRequest *)moddata_client(client, webserver_md).ptr)
#define WEBSERVER(client) ((client->local && client->local->listener) ? client->local->listener->webserver : NULL)
#define reset_handshake_timeout(client, delta) do { client->local->creationtime = TStime() - iConf.handshake_timeout + delta; } while(0)
#define WSU(client) ((WebSocketUser *)moddata_client(client, websocket_md).ptr)
/* Forward declarations */
int webserver_packet_out(Client *from, Client *to, Client *intended_to, char **msg, int *length);
int webserver_packet_in(Client *client, const char *readbuf, int *length);
void webserver_webrequest_mdata_free(ModData *m);
int webserver_handle_packet(Client *client, const char *readbuf, int length);
int webserver_handle_handshake(Client *client, const char *readbuf, int *length);
int webserver_handle_request_header(Client *client, const char *readbuf, int *length);
void _webserver_send_response(Client *client, int status, char *msg);
void _webserver_close_client(Client *client);
int _webserver_handle_body(Client *client, WebRequest *web, const char *readbuf, int length);
void parse_proxy_header(Client *client);
/* Global variables */
ModDataInfo *webserver_md; /* (by us) */
ModDataInfo *websocket_md; /* (external module, looked up)*/
MOD_TEST()
{
MARK_AS_OFFICIAL_MODULE(modinfo);
EfunctionAddVoid(modinfo->handle, EFUNC_WEBSERVER_SEND_RESPONSE, _webserver_send_response);
EfunctionAddVoid(modinfo->handle, EFUNC_WEBSERVER_CLOSE_CLIENT, _webserver_close_client);
EfunctionAdd(modinfo->handle, EFUNC_WEBSERVER_HANDLE_BODY, _webserver_handle_body);
return MOD_SUCCESS;
}
MOD_INIT()
{
ModDataInfo mreq;
MARK_AS_OFFICIAL_MODULE(modinfo);
//HookAdd(modinfo->handle, HOOKTYPE_PACKET, INT_MAX, webserver_packet_out);
HookAdd(modinfo->handle, HOOKTYPE_RAWPACKET_IN, INT_MIN, webserver_packet_in);
memset(&mreq, 0, sizeof(mreq));
mreq.name = "web";
mreq.serialize = NULL;
mreq.unserialize = NULL;
mreq.free = webserver_webrequest_mdata_free;
mreq.sync = 0;
mreq.type = MODDATATYPE_CLIENT;
webserver_md = ModDataAdd(modinfo->handle, mreq);
return MOD_SUCCESS;
}
MOD_LOAD()
{
websocket_md = findmoddata_byname("websocket", MODDATATYPE_CLIENT);
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
return MOD_SUCCESS;
}
/** UnrealIRCd internals: free WebRequest object. */
void webserver_webrequest_mdata_free(ModData *m)
{
WebRequest *wsu = (WebRequest *)m->ptr;
if (wsu)
{
safe_free(wsu->uri);
free_nvplist(wsu->headers);
safe_free(wsu->lefttoparse);
safe_free(wsu->request_buffer);
safe_free(wsu->forwarded);
safe_free(m->ptr);
}
}
/** Outgoing packet hook.
* Do we need this?
*/
int webserver_packet_out(Client *from, Client *to, Client *intended_to, char **msg, int *length)
{
static char utf8buf[510];
if (MyConnect(to) && WEB(to))
{
// TODO: Inhibit all?
// Websocket can override though?
return 0;
}
return 0;
}
HttpMethod webserver_get_method(const char *buf)
{
if (str_starts_with_case_sensitive(buf, "HEAD "))
return HTTP_METHOD_HEAD;
if (str_starts_with_case_sensitive(buf, "GET "))
return HTTP_METHOD_GET;
if (str_starts_with_case_sensitive(buf, "PUT "))
return HTTP_METHOD_PUT;
if (str_starts_with_case_sensitive(buf, "POST "))
return HTTP_METHOD_POST;
return HTTP_METHOD_NONE; /* invalid */
}
void webserver_possible_request(Client *client, const char *buf, int len)
{
HttpMethod method;
if (len < 8)
return;
/* Probably redundant, but just to be sure, if already tagged, then don't change it! */
if (WEB(client))
return;
method = webserver_get_method(buf);
if (method == HTTP_METHOD_NONE)
return; /* invalid */
moddata_client(client, webserver_md).ptr = safe_alloc(sizeof(WebRequest));
WEB(client)->method = method;
/* Set some default values: */
WEB(client)->content_length = -1;
WEB(client)->config_max_request_buffer_size = 4096; /* 4k */
}
/** Incoming packet hook. This processes web requests.
* NOTE The different return values:
* -1 means: don't touch this client anymore, it has or might have been killed!
* 0 means: don't process this data, but you can read another packet if you want
* >0 means: process this data (regular IRC data, non-web stuff)
*/
int webserver_packet_in(Client *client, const char *readbuf, int *length)
{
if ((client->local->traffic.messages_received == 0) && WEBSERVER(client))
webserver_possible_request(client, readbuf, *length);
if (!WEB(client))
return 1; /* "normal" IRC client */
if (!WEBSERVER(client))
return 0; /* handler is gone!? */
if (WEB(client)->request_header_parsed)
return WEBSERVER(client)->handle_body(client, WEB(client), readbuf, *length);
/* else.. */
return webserver_handle_request_header(client, readbuf, length);
}
/** Helper function to parse the HTTP header consisting of multiple 'Key: value' pairs */
int webserver_handshake_helper(char *buffer, int len, char **key, char **value, char **lastloc, int *lastloc_len, int *end_of_request)
{
static char buf[32768], *nextptr;
static int buflen;
char *p;
char *k = NULL, *v = NULL;
int foundlf = 0;
if (buffer)
{
/* Initialize */
if (len > sizeof(buf) - 1)
len = sizeof(buf) - 1;
buflen = len;
memcpy(buf, buffer, len);
buf[len] = '\0';
nextptr = buf;
}
*end_of_request = 0;
*lastloc_len = 0;
p = nextptr;
if (!p)
{
*key = *value = NULL;
return 0; /* done processing data */
}
if (!strncmp(p, "\n", 1) || !strncmp(p, "\r\n", 2))
{
*key = *value = NULL;
*end_of_request = 1;
return 0;
}
/* Note: p *could* point to the NUL byte ('\0') */
/* Special handling for GET line itself. */
if (webserver_get_method(p) != HTTP_METHOD_NONE)
{
k = "REQUEST";
p = strchr(p, ' ') + 1; /* space (0x20) is guaranteed to be there, see strncmp above */
v = p; /* SET VALUE */
nextptr = NULL; /* set to "we are done" in case next for loop fails */
for (; *p; p++)
{
if (*p == ' ')
{
*p = '\0'; /* terminate before "HTTP/1.X" part */
}
else if (*p == '\r')
{
*p = '\0'; /* eat silently, but don't consider EOL */
}
else if (*p == '\n')
{
*p = '\0';
nextptr = p+1; /* safe, there is data or at least a \0 there */
break;
}
}
*key = k;
*value = v;
return 1;
}
/* Header parsing starts here.
* Example line "Host: www.unrealircd.org"
*/
k = p; /* SET KEY */
/* First check if the line contains a terminating \n. If not, don't process it
* as it may have been a cut header.
*/
for (; *p; p++)
{
if (*p == '\n')
{
foundlf = 1;
break;
}
}
if (!foundlf)
{
*key = *value = NULL;
*lastloc = k;
*lastloc_len = buflen - (k - buf);
/* unreal_log(ULOG_DEBUG, "webserver", "WEBSERVER_FRAMING", NULL,
"Framing: processed $bytes_processed, remaining $bytes_remaining of $bytes_total",
log_data_integer("bytes_processed", (int)(k - buf)),
log_data_integer("bytes_remaining", *lastloc_len),
log_data_integer("bytes_total", buflen)); */
return 0;
}
p = k;
for (; *p; p++)
{
if ((*p == '\n') || (*p == '\r'))
{
/* Reached EOL but 'value' not found */
*p = '\0';
break;
}
if (*p == ':')
{
*p++ = '\0';
if (*p++ != ' ')
break; /* missing mandatory space after ':' */
v = p; /* SET VALUE */
nextptr = NULL; /* set to "we are done" in case next for loop fails */
for (; *p; p++)
{
if (*p == '\r')
{
*p = '\0'; /* eat silently, but don't consider EOL */
}
else if (*p == '\n')
{
*p = '\0';
nextptr = p+1; /* safe, there is data or at least a \0 there */
break;
}
}
/* A key-value pair was succesfully parsed, return it */
*key = k;
*value = v;
return 1;
}
}
/* Fatal parse error */
*key = *value = NULL;
return 0;
}
/** Check if there is any data at the end of the request */
char *find_end_of_request(char *header, int totalsize, int *remaining_bytes)
{
char *nextframe1;
char *nextframe2;
char *nextframe = NULL;
// find first occurance, yeah this is just stupid, but it works.
nextframe1 = strstr(header, "\r\n\r\n"); // = +4
nextframe2 = strstr(header, "\n\n"); // = +2
if (nextframe1 && nextframe2)
{
if (nextframe1 < nextframe2)
{
nextframe = nextframe1 + 4;
} else {
nextframe = nextframe2 + 2;
}
} else
if (nextframe1)
{
nextframe = nextframe1 + 4;
} else
if (nextframe2)
{
nextframe = nextframe2 + 2;
}
if (nextframe)
{
*remaining_bytes = totalsize - (nextframe - header);
if (*remaining_bytes > 0)
return nextframe;
}
return NULL;
}
/** Handle HTTP request
*/
int webserver_handle_request_header(Client *client, const char *readbuf, int *length)
{
char *key, *value;
int r, end_of_request;
char *netbuf;
char *lastloc = NULL;
int lastloc_len = 0;
int totalsize;
totalsize = WEB(client)->lefttoparselen + *length;
netbuf = safe_alloc(totalsize+1);
if (WEB(client)->lefttoparse)
{
memcpy(netbuf, WEB(client)->lefttoparse, WEB(client)->lefttoparselen);
memcpy(netbuf + WEB(client)->lefttoparselen, readbuf, *length);
} else {
memcpy(netbuf, readbuf, *length);
}
safe_free(WEB(client)->lefttoparse);
WEB(client)->lefttoparselen = 0;
// remember to always safe_free(netbuf); below BEFORE RETURNING !!!
/** Now step through the lines.. **/
for (r = webserver_handshake_helper(netbuf, totalsize, &key, &value, &lastloc, &lastloc_len, &end_of_request);
r;
r = webserver_handshake_helper(NULL, 0, &key, &value, &lastloc, &lastloc_len, &end_of_request))
{
if (BadPtr(value))
continue; /* skip empty values */
if (!strcasecmp(key, "REQUEST"))
{
safe_strdup(WEB(client)->uri, value);
} else
{
if (!strcasecmp(key, "Content-Length"))
{
WEB(client)->content_length = atoll(value);
} else
if (!strcasecmp(key, "Transfer-Encoding"))
{
if (!strcasecmp(value, "chunked"))
WEB(client)->transfer_encoding = TRANSFER_ENCODING_CHUNKED;
}
add_nvplist(&WEB(client)->headers, WEB(client)->num_headers, key, value);
}
}
if (end_of_request)
{
int n;
int remaining_bytes = 0;
char *nextframe;
/* Some sanity checks */
if (!WEB(client)->uri)
{
webserver_send_response(client, 400, "Malformed HTTP request");
safe_free(netbuf);
return -1;
}
WEB(client)->request_header_parsed = 1;
parse_proxy_header(client);
n = WEBSERVER(client)->handle_request(client, WEB(client));
if ((n <= 0) || IsDead(client))
{
safe_free(netbuf);
return n; /* byebye */
}
/* There could be data directly after the request header (eg for
* a POST or PUT), check for it here so it isn't lost.
*/
nextframe = find_end_of_request(netbuf, totalsize, &remaining_bytes);
if (nextframe)
{
int n = WEBSERVER(client)->handle_body(client, WEB(client), nextframe, remaining_bytes);
safe_free(netbuf);
return n;
}
safe_free(netbuf);
return 0;
}
if (lastloc && lastloc_len)
{
/* Last line was cut somewhere, save it for next round. */
WEB(client)->lefttoparselen = lastloc_len;
WEB(client)->lefttoparse = safe_alloc(lastloc_len);
memcpy(WEB(client)->lefttoparse, lastloc, lastloc_len);
}
safe_free(netbuf);
return 0; /* don't let UnrealIRCd process this */
}
/** Send a HTTP(S) response.
* @param client Client to send to
* @param status HTTP status code
* @param msg The message body.
* @note if 'msgs' is NULL then don't close the connection.
*/
void _webserver_send_response(Client *client, int status, char *msg)
{
char buf[512];
char *statusmsg = "???";
if (status == 200)
statusmsg = "OK";
else if (status == 201)
statusmsg = "Created";
else if (status == 500)
statusmsg = "Internal Server Error";
else if (status == 400)
statusmsg = "Bad Request";
else if (status == 401)
statusmsg = "Unauthorized";
else if (status == 403)
statusmsg = "Forbidden";
else if (status == 404)
statusmsg = "Not Found";
else if (status == 416)
statusmsg = "Range Not Satisfiable";
snprintf(buf, sizeof(buf),
"HTTP/1.1 %d %s\r\nServer: %s\r\nConnection: close\r\n\r\n",
status, statusmsg, WEB_SOFTWARE);
if (msg)
{
strlcat(buf, msg, sizeof(buf));
strlcat(buf, "\n", sizeof(buf));
}
dbuf_put(&client->local->sendQ, buf, strlen(buf));
if (msg)
webserver_close_client(client);
}
/** Close a web client softly, after data has been sent. */
void _webserver_close_client(Client *client)
{
send_queued(client);
if (DBufLength(&client->local->sendQ) == 0)
{
exit_client(client, NULL, "End of request");
//dead_socket(client, "");
} else {
send_queued(client);
reset_handshake_timeout(client, WEB_CLOSE_TIME);
}
}
int webserver_handle_body_append_buffer(Client *client, const char *buf, int len)
{
/* Guard.. */
if (len <= 0)
{
dead_socket(client, "HTTP request error");
return 0;
}
if (WEB(client)->request_buffer)
{
long long newsize = WEB(client)->request_buffer_size + len + 1;
if (newsize > WEB(client)->config_max_request_buffer_size)
{
/* We would overflow */
unreal_log(ULOG_WARNING, "webserver", "HTTP_BODY_TOO_LARGE", client,
"[webserver] Client $client: request body too large ($length)",
log_data_integer("length", newsize));
dead_socket(client, "");
return 0;
}
WEB(client)->request_buffer = realloc(WEB(client)->request_buffer, newsize);
} else
{
if (len + 1 > WEB(client)->config_max_request_buffer_size)
{
/* We would overflow */
unreal_log(ULOG_WARNING, "webserver", "HTTP_BODY_TOO_LARGE", client,
"[webserver] Client $client: request body too large ($length)",
log_data_integer("length", len+1));
dead_socket(client, "");
return 0;
}
WEB(client)->request_buffer = malloc(len+1);
}
memcpy(WEB(client)->request_buffer + WEB(client)->request_buffer_size, buf, len);
WEB(client)->request_buffer_size += len;
WEB(client)->request_buffer[WEB(client)->request_buffer_size] = '\0';
return 1;
}
/** Handle HTTP body parsing, eg for a PUT request, concatting it all together.
* @param client The client
* @param web The WEB(client)
* @param readbuf Packet in the read buffer
* @param pktsize Packet size of the read buffer
* @return 1 to continue processing, 0 if client is killed.
*/
int _webserver_handle_body(Client *client, WebRequest *web, const char *readbuf, int pktsize)
{
char *buf;
long long n;
char *free_this_buffer = NULL;
if (WEB(client)->transfer_encoding == TRANSFER_ENCODING_NONE)
{
if (!webserver_handle_body_append_buffer(client, readbuf, pktsize))
return 0;
if ((WEB(client)->content_length >= 0) &&
(WEB(client)->request_buffer_size >= WEB(client)->content_length))
{
WEB(client)->request_body_complete = 1;
}
return 1;
}
/* Fill 'buf' nd set 'buflen' with what we had + what we have now.
* Makes things easy.
*/
if (WEB(client)->lefttoparse)
{
n = WEB(client)->lefttoparselen + pktsize;
free_this_buffer = buf = safe_alloc(n);
memcpy(buf, WEB(client)->lefttoparse, WEB(client)->lefttoparselen);
memcpy(buf+WEB(client)->lefttoparselen, readbuf, pktsize);
safe_free(WEB(client)->lefttoparse);
WEB(client)->lefttoparselen = 0;
} else {
n = pktsize;
free_this_buffer = buf = safe_alloc(n);
memcpy(buf, readbuf, n);
}
/* Chunked transfers.. yayyyy.. */
while (n > 0)
{
if (WEB(client)->chunk_remaining > 0)
{
/* Eat it */
int eat = MIN(WEB(client)->chunk_remaining, n);
if (!webserver_handle_body_append_buffer(client, buf, eat))
{
/* fatal error such as size exceeded */
safe_free(free_this_buffer);
return 0;
}
n -= eat;
buf += eat;
WEB(client)->chunk_remaining -= eat;
} else
{
int gotlf = 0;
int i;
/* First check if it is a (trailing) empty line,
* eg from a previous chunk. Skip over.
*/
if ((n >= 2) && !strncmp(buf, "\r\n", 2))
{
buf += 2;
n -= 2;
} else
if ((n >= 1) && !strncmp(buf, "\n", 1))
{
buf++;
n--;
}
/* Now we are (possibly) at the chunk size line,
* this is or example '7f' + newline.
* So first, check if we have a newline at all.
*/
for (i=0; i < n; i++)
{
if (buf[i] == '\n')
{
gotlf = 1;
break;
}
}
if (!gotlf)
{
/* The line telling us the chunk size is incomplete,
* as it does not contain an \n. Wait for more data
* from the network socket.
*/
if (n > 0)
{
/* Store what we have first.. */
WEB(client)->lefttoparselen = n;
WEB(client)->lefttoparse = safe_alloc(n);
memcpy(WEB(client)->lefttoparse, buf, n);
}
safe_free(free_this_buffer);
return 1; /* WE WANT MORE! */
}
buf[i] = '\0'; /* cut at LF */
i++; /* point to next data */
WEB(client)->chunk_remaining = strtol(buf, NULL, 16);
if (WEB(client)->chunk_remaining < 0)
{
unreal_log(ULOG_WARNING, "webserver", "WEB_NEGATIVE_CHUNK", client,
"Webrequest from $client: Negative chunk encountered");
safe_free(free_this_buffer);
dead_socket(client, "");
return 0;
}
if (WEB(client)->chunk_remaining == 0)
{
/* DONE! */
WEB(client)->request_body_complete = 1;
safe_free(free_this_buffer);
return 1;
}
buf += i;
n -= i;
}
}
safe_free(free_this_buffer);
return 1;
}
/** If a valid Forwarded http header is received from a trusted source (proxy server),
* this function will extract remote IP address and secure (https) status from it.
* If more than one field with same name is received, we'll accept the last one.
*/
void do_parse_forwarded_header(const char *input, HTTPForwardedHeader *forwarded)
{
char *buf = NULL;
char *name, *value, *p = NULL, *x;
safe_strdup(buf, input);
for (name = strtoken(&p, buf, ";,"); name; name = strtoken(&p, NULL, ";,"))
{
skip_whitespace(&name);
value = strchr(name, '=');
if (value)
*value++ = '\0';
if (!value)
continue; /* we don't use value-less items atm anyway */
/* Remove quotes - if any */
if (*value == '"')
{
value++;
x = strchr(value, '"');
if (x)
*x = '\0';
}
if (!strcasecmp(name, "for"))
{
/* IPv6 is in brackets, so cut it off */
if (*value == '[')
{
value++;
char *x = strchr(value, ']');
if (x)
*x = '\0';
/* ^^ this cuts off everything after ']', which
* may also cut off the optional local port,
* but that is fine: we don't use it.
*/
} else
if ((x = strchr(value, ':')))
{
/* For non-IPv6 ip:port, cut off at the ':',
* so we only have IP.
*/
*x = '\0';
}
strlcpy(forwarded->ip, value, sizeof(forwarded->ip));
} else
if (!strcasecmp(name, "proto"))
{
if (!strcasecmp(value, "https"))
{
forwarded->secure = 1;
} else if (!strcasecmp(value, "http"))
{
forwarded->secure = 0;
} else
{
/* ignore unknown value */
}
}
}
safe_free(buf);
}
/** If a valid X-Forwarded-For http header is received from a trusted source (proxy server),
* this function will extract remote IP address and secure (https) status from it.
* If more than one IP is received, we'll accept the last one.
*/
void do_parse_x_forwarded_for_header(const char *input, HTTPForwardedHeader *forwarded)
{
char *buf = NULL;
char *name, *value, *p = NULL;
safe_strdup(buf, input);
for (name = strtoken(&p, buf, ","); name; name = strtoken(&p, NULL, ","))
{
skip_whitespace(&name);
strlcpy(forwarded->ip, name, sizeof(forwarded->ip));
}
safe_free(buf);
}
void do_parse_x_forwarded_proto_header(const char *value, HTTPForwardedHeader *forwarded)
{
if (!strcmp(value, "https"))
forwarded->secure = 1;
else if (!strcmp(value, "http"))
forwarded->secure = 0;
}
void webserver_handle_proxy(Client *client, ConfigItem_proxy *proxy)
{
HTTPForwardedHeader *forwarded;
char oldip[64];
NameValuePrioList *header;
/* Set up 'forwarded' variable */
if (WEB(client)->forwarded == NULL)
{
WEB(client)->forwarded = safe_alloc(sizeof(HTTPForwardedHeader));
} else {
memset(WEB(client)->forwarded, 0, sizeof(HTTPForwardedHeader));
}
forwarded = WEB(client)->forwarded;
/* Go through the headers and parse them */
for (header = WEB(client)->headers; header; header = header->next)
{
if (proxy->type == PROXY_FORWARDED)
{
if (!strcasecmp(header->name, "Forwarded"))
do_parse_forwarded_header(header->value, forwarded);
} else
if (proxy->type == PROXY_X_FORWARDED)
{
if (!strcasecmp(header->name, "X-Forwarded-For"))
do_parse_x_forwarded_for_header(header->value, forwarded);
else if (!strcasecmp(header->name, "X-Forwarded-Proto"))
do_parse_x_forwarded_proto_header(header->value, forwarded);
} else
if (proxy->type == PROXY_CLOUDFLARE)
{
/* This is a mix of CF-Connecting-IP and X-Forwarded-Proto */
if (!strcasecmp(header->name, "CF-Connecting-IP"))
do_parse_x_forwarded_for_header(header->value, forwarded);
else if (!strcasecmp(header->name, "X-Forwarded-Proto"))
do_parse_x_forwarded_proto_header(header->value, forwarded);
}
}
#ifdef DEBUGMODE
unreal_log(ULOG_DEBUG, "webserver", "FORWARDING_INFO", client,
"For client $client.details forwarding IP is $ip and is $secure",
log_data_string("ip", forwarded->ip),
log_data_string("secure", forwarded->secure ? "secure" : "insecure"));
#endif
/* check header values */
if (!is_valid_ip(forwarded->ip))
{
unreal_log(ULOG_WARNING, "webserver", "MISSING_PROXY_HEADER", client,
"Client on proxy $client.ip has matching proxy { } block "
"but the proxy did not send a valid forwarded header. "
"The IP of the user is now the proxy IP $client.ip (bad!).");
// TODO: or should we reject the user entirely?
return;
}
/* store data / set new IP */
strlcpy(oldip, client->ip, sizeof(oldip));
safe_strdup(client->ip, forwarded->ip);
strlcpy(client->local->sockhost, forwarded->ip, sizeof(client->local->sockhost)); /* in case dns lookup fails or is disabled */
/* restart DNS & ident lookups */
start_dns_and_ident_lookup(client);
RunHook(HOOKTYPE_IP_CHANGE, client, oldip);
}
/** Parse proxy headers (if any) and run proxy ip change routines (if needed).
* This is called after receiving the HTTP request, right before passing
* the request on to next handler (eg. the 'websocket' module).
*/
void parse_proxy_header(Client *client)
{
ConfigItem_proxy *proxy;
for (proxy = conf_proxy; proxy; proxy = proxy->next)
{
if (IsWebProxy(proxy->type) &&
user_allowed_by_security_group(client, proxy->mask))
{
webserver_handle_proxy(client, proxy);
return;
}
}
}