summaryrefslogtreecommitdiff
path: root/src/reqs.c
diff options
context:
space:
mode:
authorRobert James Kaes <rjkaes@users.sourceforge.net>2001-09-14 04:56:29 +0000
committerRobert James Kaes <rjkaes@users.sourceforge.net>2001-09-14 04:56:29 +0000
commitc6d2e0f7ffa7396b39118241d755a002b997ea9f (patch)
tree4ece2dddebb59fc7ecbe41b714eb736f589ae6b1 /src/reqs.c
parentbce9e6601e5e754793d55d761dc5a657a2e97aee (diff)
downloadtinyproxy-c6d2e0f7ffa7396b39118241d755a002b997ea9f.tar.gz
tinyproxy-c6d2e0f7ffa7396b39118241d755a002b997ea9f.zip
Massive changes. Split process_method() into a bunch of smaller files and
changed the parsing code from REGEX and uri.c to a simplier sscanf() method. Also, include code to handle SSL connections, but that's not quite working yet.
Diffstat (limited to '')
-rw-r--r--src/reqs.c491
1 files changed, 299 insertions, 192 deletions
diff --git a/src/reqs.c b/src/reqs.c
index 19d8cf3..b5a2800 100644
--- a/src/reqs.c
+++ b/src/reqs.c
@@ -1,4 +1,4 @@
-/* $Id: reqs.c,v 1.23 2001-09-12 03:33:15 rjkaes Exp $
+/* $Id: reqs.c,v 1.24 2001-09-14 04:56:29 rjkaes Exp $
*
* This is where all the work in tinyproxy is actually done. Incoming
* connections have a new thread created for them. The thread then
@@ -34,22 +34,13 @@
#include "reqs.h"
#include "sock.h"
#include "stats.h"
-#include "uri.h"
#include "utils.h"
-#define HTTPPATTERN "^([a-z]+)[ \t]+([^ \t]+)([ \t]+(HTTP/[0-9]+\\.[0-9]+))?"
-#define NMATCH 4
-#define METHOD_IND 1
-#define URI_IND 2
-#define VERSION_MARK 3
-#define VERSION_IND 4
-
#define HTTP400ERROR "Unrecognizable request. Only HTTP is allowed."
#define HTTP500ERROR "Unable to connect to remote server."
#define HTTP503ERROR "Internal server error."
#define LINE_LENGTH (MAXBUFFSIZE / 3)
-#define HTTP_PORT 80
/*
* Write the buffer to the socket. If an EINTR occurs, pick up and try
@@ -105,228 +96,322 @@ static inline void trim(char *string, unsigned int len)
}
/*
- * Parse a client HTTP request and then establish connection.
+ * Read in the first line from the client (the request line for HTTP
+ * connections. The request line is allocated from the heap, but it must
+ * be freed in another function.
*/
-static int process_method(struct conn_s *connptr)
+static char *read_request_line(struct conn_s *connptr)
{
- URI *uri = NULL;
- char *inbuf;
- char *buffer = NULL, *request = NULL, *port = NULL;
-
- regex_t preg;
- regmatch_t pmatch[NMATCH];
-
- size_t request_len;
+ char *request_buffer;
size_t len;
- int fd, port_no = HTTP_PORT;
-
- char peer_ipaddr[PEER_IP_LENGTH];
- getpeer_ip(connptr->client_fd, peer_ipaddr);
-
- inbuf = safemalloc(LINE_LENGTH);
- if (!inbuf) {
- log_message(LOG_ERR, "Could not allocate memory in 'process_method'.");
- return -2;
+ request_buffer = safemalloc(LINE_LENGTH);
+ if (!request_buffer) {
+ log_message(LOG_ERR, "Could not allocate memory in 'read_request_line'");
+ return NULL;
}
- len = readline(connptr->client_fd, inbuf, LINE_LENGTH);
+ len = readline(connptr->client_fd, request_buffer, LINE_LENGTH);
if (len <= 0) {
- log_message(LOG_ERR, "Client [%s] closed socket before read.", peer_ipaddr);
- update_stats(STAT_BADCONN);
- safefree(inbuf);
- return -2;
+ log_message(LOG_ERR, "Client (file descriptor: %d) closed socket before read.", connptr->client_fd);
+ safefree(request_buffer);
+ return NULL;
}
/*
- * Strip the newline and character return from the string.
+ * Strip the new line and character return from the string.
*/
- trim(inbuf, len);
+ trim(request_buffer, len);
- log_message(LOG_CONN, "Request: %s", inbuf);
+ log_message(LOG_CONN, "Request (file descriptor %d): %s",
+ connptr->client_fd, request_buffer);
- if (regcomp(&preg, HTTPPATTERN, REG_EXTENDED | REG_ICASE) != 0) {
- log_message(LOG_ERR, "Regular Expression compiling error.");
- httperr(connptr, 503, HTTP503ERROR);
- update_stats(STAT_BADCONN);
- goto EARLY_EXIT;
+ return request_buffer;
+}
+
+/*
+ * This structure holds the information pulled from a URL request.
+ */
+struct request_s {
+ char *host;
+ char *path;
+ int port;
+};
+
+/*
+ * Pull the information out of the URL line.
+ */
+static int extract_http_url(const char *url, struct request_s *request)
+{
+ request->host = safemalloc(strlen(url) + 1);
+ request->path = safemalloc(strlen(url) + 1);
+
+ if (!request->host || !request->path) {
+ log_message(LOG_ERR, "Could not allocate memory in 'extract_http_url'");
+ safefree(request->host);
+ safefree(request->path);
+
+ return -1;
}
- if (regexec(&preg, inbuf, NMATCH, pmatch, 0) != 0) {
- log_message(LOG_ERR, "Regular Expression search error.");
- regfree(&preg);
- httperr(connptr, 503, HTTP503ERROR);
- update_stats(STAT_BADCONN);
- goto EARLY_EXIT;
+
+ if (sscanf(url, "http://%[^:/]:%d%s", request->host, &request->port, request->path) == 3)
+ ;
+ else if (sscanf(url, "http://%[^/]%s", request->host, request->path) == 2)
+ request->port = 80;
+ else if (sscanf(url, "http://%[^:/]:%d", request->host, &request->port) == 2)
+ strcpy(request->path, "/");
+ else if (sscanf(url, "http://%[^/]", request->host) == 1) {
+ request->port = 80;
+ strcpy(request->path, "/");
+ } else {
+ log_message(LOG_ERR, "Can't parse URL.");
+
+ safefree(request->host);
+ safefree(request->path);
+
+ return -1;
}
- regfree(&preg);
- /*
- * Test for a simple request, or a request from version 0.9
- * - rjkaes
- */
- if (pmatch[VERSION_MARK].rm_so == -1
- || !strncasecmp("http/0.9", inbuf + pmatch[VERSION_IND].rm_so, 8))
- connptr->simple_req = TRUE;
-
+ return 0;
+}
- if (pmatch[METHOD_IND].rm_so == -1 || pmatch[URI_IND].rm_so == -1) {
- log_message(LOG_ERR, "Incomplete request line from [%s].",
- peer_ipaddr);
- httperr(connptr, 400, HTTP400ERROR);
- update_stats(STAT_BADCONN);
- goto EARLY_EXIT;
+/*
+ * Extract the URL from a SSL connection.
+ */
+static int extract_ssl_url(const char *url, struct request_s *request)
+{
+ request->host = safemalloc(strlen(url) + 1);
+
+ if (!request->host) {
+ log_message(LOG_ERR, "Could not allocate memory in 'extract_https_url'");
+
+ return -1;
}
- len = pmatch[URI_IND].rm_eo - pmatch[URI_IND].rm_so;
- if (!(buffer = safemalloc(len + 1))) {
- log_message(LOG_ERR,
- "Could not allocate memory for request from [%s].",
- peer_ipaddr);
- httperr(connptr, 503, HTTP503ERROR);
- update_stats(STAT_BADCONN);
- goto EARLY_EXIT;
+ if (sscanf(url, "%[^:]:%d", request->host, &request->port) == 2)
+ ;
+ else if (sscanf(url, "%s", request->host) == 1)
+ request->port = 443;
+ else {
+ log_message(LOG_ERR, "Can't parse URL.");
+
+ safefree(request->host);
+ return -1;
}
- memcpy(buffer, inbuf + pmatch[URI_IND].rm_so, len);
- buffer[len] = '\0';
- if (!(uri = explode_uri(buffer))) {
- safefree(buffer);
+
+ return 0;
+}
+
+/*
+ * Create a connection for HTTP connections.
+ */
+static int establish_http_connection(struct conn_s *connptr,
+ const char *method,
+ const char *protocol,
+ struct request_s *request)
+{
+ char *request_line;
+ size_t request_len;
+
+ request_len = strlen(method) + strlen(protocol) + strlen(request->path) + 13;
+
+ request_line = safemalloc(request_len);
+ if (!request_line) {
+ log_message(LOG_ERR, "Could not allocate memory in 'process_request'");
httperr(connptr, 503, HTTP503ERROR);
- update_stats(STAT_BADCONN);
- goto EARLY_EXIT;
+
+ return -1;
}
- safefree(buffer);
- if (!uri->scheme || strcasecmp(uri->scheme, "http") != 0) {
- char *error_string;
- if (uri->scheme) {
- size_t error_string_len = strlen(uri->scheme) + 64;
- error_string = safemalloc(error_string_len);
- if (!error_string) {
- log_message(LOG_ERR,
- "Could not allocate memory for request from [%s].",
- peer_ipaddr);
- goto COMMON_EXIT;
- }
- snprintf(error_string, error_string_len,
- "Invalid scheme (%s). Only HTTP is allowed.",
- uri->scheme);
- } else {
- error_string =
- strdup("Invalid scheme (NULL). Only HTTP is allowed.");
- if (!error_string) {
- log_message(LOG_ERR,
- "Could not allocate memory for request from [%s].",
- peer_ipaddr);
- goto COMMON_EXIT;
- }
- }
+ strlcpy(request_line, method, request_len);
+ strlcat(request_line, " ", request_len);
+ strlcat(request_line, request->path, request_len);
+ strlcat(request_line, " ", request_len);
+ strlcat(request_line, "HTTP/1.0", request_len);
+ strlcat(request_line, "\r\n", request_len);
- httperr(connptr, 400, error_string);
- safefree(error_string);
- update_stats(STAT_BADCONN);
- goto COMMON_EXIT;
+ if (safe_write(connptr->server_fd, request_line, strlen(request_line)) < 0) {
+ safefree(request_line);
+
+ return -1;
}
+ safefree(request_line);
+
+ /*
+ * Send headers
+ */
+ if (safe_write(connptr->server_fd, "Host: ", 6) < 0) {
+ return -1;
+ }
+ if (safe_write(connptr->server_fd, request->host, strlen(request->host)) < 0) {
+ return -1;
+ }
+
+ if (safe_write(connptr->server_fd, "\r\n", 2) < 0)
+ return -1;
+
+ /*
+ * Send the Connection header since we don't support persistant
+ * connections.
+ */
+ if (safe_write(connptr->server_fd, "Connection: close\r\n", 19) < 0)
+ return -1;
+
+ return 0;
+}
- if (!uri->authority) {
- httperr(connptr, 400, "Invalid authority.");
- update_stats(STAT_BADCONN);
- goto COMMON_EXIT;
+/*
+ * Break the request line apart and figure out where to connect and
+ * build a new request line. Finally connect to the remote server.
+ */
+static int process_request(struct conn_s *connptr, char *request_line)
+{
+ char *method;
+ char *url;
+ char *protocol;
+
+ struct request_s request;
+
+ int ret;
+
+ size_t request_len;
+
+ /* NULL out all the fields so free's don't cause segfaults. */
+ memset(&request, 0, sizeof(struct request_s));
+
+ request_len = strlen(request_line) + 1;
+
+ method = safemalloc(request_len);
+ url = safemalloc(request_len);
+ protocol = safemalloc(request_len);
+
+ if (!method || !url || !protocol) {
+ log_message(LOG_ERR, "Could not allocate memory in 'process_request'");
+ safefree(method);
+ safefree(url);
+ safefree(protocol);
+ return -1;
}
- if ((strlen(config.stathost) > 0) &&
- strcasecmp(uri->authority, config.stathost) == 0) {
- showstats(connptr);
- goto COMMON_EXIT;
+ ret = sscanf(request_line, "%[^ ] %[^ ] %[^ ]", method, url, protocol);
+ if (ret < 2) {
+ log_message(LOG_ERR, "Bad Request on file descriptor %d", connptr->client_fd);
+ httperr(connptr, 400, "Bad Request. No request found.");
+
+ safefree(method);
+ safefree(url);
+ safefree(protocol);
+
+ return -1;
+ } else if (ret == 2) {
+ connptr->simple_req = TRUE;
}
- if ((port = strchr(uri->authority, ':'))) {
- *port++ = '\0';
- if (strlen(port) > 0)
- port_no = atoi(port);
+ if (!url) {
+ log_message(LOG_ERR, "Null URL on file descriptor %d", connptr->client_fd);
+ httperr(connptr, 400, "Bad Request. Null URL.");
+
+ safefree(method);
+ safefree(url);
+ safefree(protocol);
+
+ return -1;
}
-#ifdef FILTER_ENABLE
- /* Filter domains out */
- if (config.filter) {
- if (filter_url(uri->authority)) {
- log_message(LOG_ERR,
- "Proxying refused on filtered domain \"%s\" from [%s].",
- uri->authority,
- peer_ipaddr);
- httperr(connptr, 404,
- "Connection to filtered domain is not allowed.");
- update_stats(STAT_DENIED);
- goto COMMON_EXIT;
+ if (strncasecmp(url, "http://", 7) == 0) {
+ /* Make sure the first four characters are lowercase */
+ memcpy(url, "http", 4);
+
+ if (extract_http_url(url, &request) < 0) {
+ httperr(connptr, 400, "Bad Request. Could not parse URL.");
+
+ safefree(method);
+ safefree(url);
+ safefree(protocol);
+
+ return -1;
}
- }
-#endif /* FILTER_ENABLE */
+ connptr->ssl = FALSE;
+ } else if (strcmp(method, "CONNECT") == 0) {
+ if (extract_ssl_url(url, &request) < 0) {
+ httperr(connptr, 400, "Bad Request. Could not parse URL.");
- /* Build a new request from the first line of the header */
- request_len = strlen(inbuf) + 1;
- if (!(request = safemalloc(request_len))) {
- log_message(LOG_ERR,
- "Could not allocate memory for request from [%s].",
- peer_ipaddr);
- httperr(connptr, 503, HTTP503ERROR);
- update_stats(STAT_BADCONN);
- goto COMMON_EXIT;
- }
-
- memcpy(request, inbuf, pmatch[METHOD_IND].rm_eo);
- request[pmatch[METHOD_IND].rm_eo] = '\0';
- strlcat(request, " ", request_len);
- if (strlen(uri->path) > 0) {
- strlcat(request, uri->path, request_len);
- if (uri->query) {
- strlcat(request, "?", request_len);
- strlcat(request, uri->query, request_len);
+ safefree(method);
+ safefree(url);
+ safefree(protocol);
+
+ return -1;
}
+ connptr->ssl = TRUE;
} else {
- strlcat(request, "/", request_len);
- }
- strlcat(request, " HTTP/1.0\r\n", request_len);
+ log_message(LOG_ERR, "Unknown URL type on file descriptor %d", connptr->client_fd);
+ httperr(connptr, 400, "Bad Request. Unknown URL type.");
- fd = opensock(uri->authority, port_no);
- if (fd < 0) {
- httperr(connptr, 500, HTTP500ERROR);
- update_stats(STAT_DENIED);
- goto COMMON_EXIT;
+ safefree(method);
+ safefree(url);
+ safefree(protocol);
+
+ return -1;
}
- connptr->server_fd = fd;
+ safefree(url);
- if (safe_write(connptr->server_fd, request, strlen(request)) < 0)
- goto COMMON_EXIT;
-
+#ifdef FILTER_ENABLE
/*
- * Send the Host: header
+ * Filter restricted domains
*/
- if (safe_write(connptr->server_fd, "Host: ", 6) < 0)
- goto COMMON_EXIT;
- if (safe_write(connptr->server_fd, uri->authority, strlen(uri->authority)) < 0)
- goto COMMON_EXIT;
- if (safe_write(connptr->server_fd, "\r\n", 2) < 0)
- goto COMMON_EXIT;
+ if (config.filter) {
+ if (filter_url(request.host)) {
+ log_message(LOG_ERR, "Proxying refused on filtered domain \"%s\"", request.host);
+ httperr(connptr, 404, "Connection to filtered domain is now allowed.");
+
+ safefree(request.host);
+ safefree(request.path);
+
+ safefree(method);
+ safefree(url);
+
+ return -1;
+ }
+ }
+#endif
/*
- * Send the Connection header since we don't support persistant
- * connections.
+ * Connect to the remote server.
*/
- if (safe_write(connptr->server_fd, "Connection: close\r\n", 19) < 0)
- goto COMMON_EXIT;
+ connptr->server_fd = opensock(request.host, request.port);
+ if (connptr->server_fd < 0) {
+ httperr(connptr, 500, HTTP500ERROR);
- safefree(inbuf);
- safefree(request);
- free_uri(uri);
- return 0;
+ safefree(request.host);
+ safefree(request.path);
-COMMON_EXIT:
- free_uri(uri);
-
-EARLY_EXIT:
- safefree(inbuf);
- safefree(request);
- return -1;
+ safefree(method);
+ safefree(protocol);
+
+ return -1;
+ }
+
+ if (!connptr->ssl) {
+ if (establish_http_connection(connptr, method, protocol, &request) < 0) {
+
+ safefree(method);
+ safefree(protocol);
+
+ safefree(request.host);
+ safefree(request.path);
+
+ return -1;
+ }
+ }
+
+ safefree(method);
+ safefree(protocol);
+
+ safefree(request.host);
+ safefree(request.path);
+
+ return 0;
}
/*
@@ -641,6 +726,8 @@ static void initialize_conn(struct conn_s *connptr)
connptr->output_message = NULL;
connptr->simple_req = FALSE;
+ connptr->ssl = FALSE;
+
update_stats(STAT_OPEN);
}
@@ -677,7 +764,10 @@ void handle_connection(int fd)
char peer_ipaddr[PEER_IP_LENGTH];
char peer_string[PEER_STRING_LENGTH];
- log_message(LOG_CONN, "Connect: %s [%s]",
+ char *request_line;
+
+ log_message(LOG_CONN, "Connect (file descriptor %d): %s [%s]",
+ fd,
getpeer_string(fd, peer_string),
getpeer_ip(fd, peer_ipaddr));
@@ -726,13 +816,22 @@ void handle_connection(int fd)
#endif /* TUNNEL_SUPPORT */
internal_proxy:
- if (process_method(connptr) < -1) {
+ request_line = read_request_line(connptr);
+ if (!request_line) {
destroy_conn(connptr);
return;
}
+ if (process_request(connptr, request_line) < 0) {
+ safefree(request_line);
+ destroy_conn(connptr);
+ return;
+ }
+
+ safefree(request_line);
+
send_error:
- if (!connptr->simple_req) {
+ if (!connptr->simple_req && !connptr->ssl) {
if (process_client_headers(connptr) < 0) {
update_stats(STAT_BADCONN);
destroy_conn(connptr);
@@ -748,10 +847,18 @@ send_error:
return;
}
- if (process_server_headers(connptr) < 0) {
- update_stats(STAT_BADCONN);
- destroy_conn(connptr);
- return;
+ if (!connptr->ssl) {
+ if (process_server_headers(connptr) < 0) {
+ update_stats(STAT_BADCONN);
+ destroy_conn(connptr);
+ return;
+ }
+ } else {
+ if (safe_write(connptr->client_fd, "HTTP/1.0 200 Connection established\r\n\r\n", 39) < 0) {
+ log_message(LOG_ERR, "Could not send SSL greeting to client.");
+ destroy_conn(connptr);
+ return;
+ }
}
relay_proxy: