Beispiel #1
0
/*
 * Break the request line apart and figure out where to connect and
 * build a new request line. Finally connect to the remote server.
 */
static struct request_s *
process_request(struct conn_s *connptr, hashmap_t hashofheaders)
{
	char *url;
	struct request_s *request;

	int ret;

	size_t request_len;

	/* NULL out all the fields so frees don't cause segfaults. */
	request = safecalloc(1, sizeof(struct request_s));
	if (!request)
		return NULL;

	request_len = strlen(connptr->request_line) + 1;

	request->method = safemalloc(request_len);
	url = safemalloc(request_len);
	request->protocol = safemalloc(request_len);

	if (!request->method || !url || !request->protocol) {
		safefree(url);
		free_request_struct(request);

		return NULL;
	}

	ret =
	    sscanf(connptr->request_line, "%[^ ] %[^ ] %[^ ]",
		   request->method, url, request->protocol);
	if (ret < 2) {
		log_message(LOG_ERR,
			    "process_request: Bad Request on file descriptor %d",
			    connptr->client_fd);
		indicate_http_error(connptr, 400, "Bad Request",
				    "detail", "Request has an invalid format",
				    "url", url,
				    NULL);

		safefree(url);
		free_request_struct(request);

		return NULL;
	}

	/* 
	 * FIXME: We need to add code for the simple HTTP/0.9 style GET
	 * request.
	 */

	if (!url) {
		log_message(LOG_ERR,
			    "process_request: Null URL on file descriptor %d",
			    connptr->client_fd);
		indicate_http_error(connptr, 400, "Bad Request",
				    "detail", "Request has an empty URL",
				    "url", url,
				    NULL);

		safefree(url);
		free_request_struct(request);

		return NULL;
	}

	if (strncasecmp(url, "http://", 7) == 0
	    || (UPSTREAM_CONFIGURED() && strncasecmp(url, "ftp://", 6) == 0)) {
		char *skipped_type = strstr(url, "//") + 2;

		if (extract_http_url(skipped_type, request) < 0) {
			indicate_http_error(connptr, 400, "Bad Request",
					    "detail", "Could not parse URL",
					    "url", url,
					    NULL);

			safefree(url);
			free_request_struct(request);

			return NULL;
		}
	} else if (strcmp(request->method, "CONNECT") == 0) {
		if (extract_ssl_url(url, request) < 0) {
			indicate_http_error(connptr, 400, "Bad Request",
					    "detail", "Could not parse URL",
					    "url", url,
					    NULL);

			safefree(url);
			free_request_struct(request);

			return NULL;
		}

		/* Verify that the port in the CONNECT method is allowed */
		if (check_allowed_connect_ports(request->port) <= 0) {
			indicate_http_error(connptr, 403, "Access violation",
					    "detail", "The CONNECT method not allowed " \
					              "with the port you tried to use.",
					    "url", url,
					    NULL);
			log_message(LOG_INFO, "Refused CONNECT method on port %d",
				    request->port);

			safefree(url);
			free_request_struct(request);

			return NULL;
		}
		
		connptr->connect_method = TRUE;
	} else {
#ifdef TRANSPARENT_PROXY
		/*
		 * This section of code is used for the transparent proxy
		 * option.  You will need to configure your firewall to
		 * redirect all connections for HTTP traffic to tinyproxy
		 * for this to work properly.
		 *
		 * This code was written by Petr Lampa <*****@*****.**>
		 */
		int length;
		char *data;
		length = hashmap_entry_by_key(hashofheaders, "host", (void **)&data);
		if (length <= 0) {
			struct sockaddr_in dest_addr;

			if (getsockname(connptr->client_fd, (struct sockaddr *)&dest_addr, &length) < 0) {
				log_message(LOG_ERR,
					    "process_request: cannot get destination IP for %d",
					    connptr->client_fd);
				indicate_http_error(connptr, 400, "Bad Request",
						    "detail", "Unknown destination",
						    "url", url,
						    NULL);
				safefree(url);
				free_request_struct(request);
				return NULL;
			} 
			request->host = safemalloc(17);
			strcpy(request->host, inet_ntoa(dest_addr.sin_addr));
			request->port = ntohs(dest_addr.sin_port);
			request->path = safemalloc(strlen(url) + 1);
			strcpy(request->path, url);
			safefree(url);
			build_url(&url, request->host, request->port, request->path);
			log_message(LOG_INFO,
				    "process_request: trans IP %s %s for %d",
				    request->method, url, connptr->client_fd);
		} else {
			request->host = safemalloc(length+1);
			if (sscanf(data, "%[^:]:%hu", request->host, &request->port) != 2) {
				strcpy(request->host, data);
				request->port = HTTP_PORT;
			}
			request->path = safemalloc(strlen(url) + 1);
			strcpy(request->path, url);
			safefree(url);
			build_url(&url, request->host, request->port, request->path);
			log_message(LOG_INFO,
				    "process_request: trans Host %s %s for %d",
				    request->method, url, connptr->client_fd);
		}
		if (config.ipAddr &&
		    strcmp(request->host, config.ipAddr) == 0) {
			log_message(LOG_ERR,
				    "process_request: destination IP is localhost %d",
				    connptr->client_fd);
			indicate_http_error(connptr, 400, "Bad Request",
					    "detail", "You tried to connect to the machine the proxy is running on",
					    "url", url,
					    NULL);
			safefree(url);
			free_request_struct(request);
			return NULL;
		}
#else
		log_message(LOG_ERR,
			    "process_request: Unknown URL type on file descriptor %d",
			    connptr->client_fd);
		indicate_http_error(connptr, 400, "Bad Request",
				    "detail", "Unknown URL type",
				    "url", url,
				    NULL);

		safefree(url);
		free_request_struct(request);

		return NULL;
#endif
	}

#ifdef FILTER_ENABLE
	/*
	 * Filter restricted domains/urls
	 */
	if (config.filter) {
		if (config.filter_url)
			ret = filter_url(url);
		else
			ret = filter_domain(request->host);

		if (ret) {
			update_stats(STAT_DENIED);

			if (config.filter_url)
				log_message(LOG_NOTICE,
					    "Proxying refused on filtered url \"%s\"",
					    url);
			else
				log_message(LOG_NOTICE,
					    "Proxying refused on filtered domain \"%s\"",
					    request->host);

			indicate_http_error(connptr, 403, "Filtered",
					    "detail", "The request you made has been filted",
					    "url", url,
					    NULL);

			safefree(url);
			free_request_struct(request);

			return NULL;
		}
	}
#endif

	safefree(url);

	/*
	 * Check to see if they're requesting the stat host
	 */
	if (config.stathost && strcmp(config.stathost, request->host) == 0) {
		log_message(LOG_NOTICE, "Request for the stathost.");
		connptr->show_stats = TRUE;

		free_request_struct(request);
		return NULL;
	}

	/*
	 * Break apart the protocol and update the connection structure.
	 */
	if (strncasecmp(request->protocol, "http", 4) == 0) {
		memcpy(request->protocol, "HTTP", 4);
		sscanf(request->protocol, "HTTP/%u.%u",
		       &connptr->protocol.major, &connptr->protocol.minor);
	}

	return request;
}
Beispiel #2
0
/*
 * This is the main drive for each connection. As you can tell, for the
 * first few steps we are using a blocking socket. If you remember the
 * older tinyproxy code, this use to be a very confusing state machine.
 * Well, no more! :) The sockets are only switched into nonblocking mode
 * when we start the relay portion. This makes most of the original
 * tinyproxy code, which was confusing, redundant. Hail progress.
 * 	- rjkaes
 */
void
handle_connection(int fd)
{
	struct conn_s *connptr;
	struct request_s *request = NULL;
	hashmap_t hashofheaders = NULL;

	char peer_ipaddr[PEER_IP_LENGTH];
	char peer_string[PEER_STRING_LENGTH];

	getpeer_information(fd, peer_ipaddr, peer_string);

	log_message(LOG_CONN, "Connect (file descriptor %d): %s [%s]",
		    fd, peer_string, peer_ipaddr);

	connptr = initialize_conn(fd, peer_ipaddr, peer_string);
	if (!connptr) {
		close(fd);
		return;
	}

	if (check_acl(fd, peer_ipaddr, peer_string) <= 0) {
		update_stats(STAT_DENIED);
		indicate_http_error(connptr, 403, "Access denied",
				    "detail", "The administrator of this proxy has not configured it to service requests from your host.",
				    NULL);
		send_http_error_message(connptr);
		destroy_conn(connptr);
		return;
	}

	if (read_request_line(connptr) < 0) {
		update_stats(STAT_BADCONN);
		indicate_http_error(connptr, 408, "Timeout",
				    "detail", "Server timeout waiting for the HTTP request from the client.",
				    NULL);
		send_http_error_message(connptr);
		destroy_conn(connptr);
		return;
	}

	/*
	 * The "hashofheaders" store the client's headers.
	 */
	if (!(hashofheaders = hashmap_create(HEADER_BUCKETS))) {
		update_stats(STAT_BADCONN);
		indicate_http_error(connptr, 503, "Internal error",
				    "detail", "An internal server error occurred while processing your request.  Please contact the administrator.",
				    NULL);
		send_http_error_message(connptr);
		destroy_conn(connptr);
		return;
	}

	/*
	 * Get all the headers from the client in a big hash.
	 */
	if (get_all_headers(connptr->client_fd, hashofheaders) < 0) {
		log_message(LOG_WARNING, "Could not retrieve all the headers from the client");
		hashmap_delete(hashofheaders);
		update_stats(STAT_BADCONN);
		destroy_conn(connptr);
		return;
	}

	request = process_request(connptr, hashofheaders);
	if (!request) {
		if (!connptr->error_variables && !connptr->show_stats) {
			update_stats(STAT_BADCONN);
			destroy_conn(connptr);
			hashmap_delete(hashofheaders);
			return;
		}
		goto send_error;
	}

	connptr->upstream_proxy = UPSTREAM_HOST(request->host);
	if (connptr->upstream_proxy != NULL) {
		if (connect_to_upstream(connptr, request) < 0) {
			goto send_error;
		}
	} else {
		connptr->server_fd = opensock(request->host, request->port);
		if (connptr->server_fd < 0) {
			indicate_http_error(connptr, 500, "Unable to connect",
					    "detail", PACKAGE " was unable to connect to the remote web server.",
					    "error", strerror(errno),
					    NULL);
			goto send_error;
		}

		log_message(LOG_CONN,
			    "Established connection to host \"%s\" using file descriptor %d.",
			    request->host, connptr->server_fd);

		if (!connptr->connect_method)
			establish_http_connection(connptr, request);
	}

      send_error:
	free_request_struct(request);

	if (process_client_headers(connptr, hashofheaders) < 0) {
		update_stats(STAT_BADCONN);
		if (!connptr->error_variables) {
			hashmap_delete(hashofheaders);
			destroy_conn(connptr);
			return;
		}
	}
	hashmap_delete(hashofheaders);

	if (connptr->error_variables) {
		send_http_error_message(connptr);
		destroy_conn(connptr);
		return;
	} else if (connptr->show_stats) {
		showstats(connptr);
		destroy_conn(connptr);
		return;
	}

	if (!connptr->connect_method || (connptr->upstream_proxy != NULL)) {
		if (process_server_headers(connptr) < 0) {
			if (connptr->error_variables)
				send_http_error_message(connptr);

			update_stats(STAT_BADCONN);
			destroy_conn(connptr);
			return;
		}
	} else {
		if (send_ssl_response(connptr) < 0) {
			log_message(LOG_ERR,
				    "handle_connection: Could not send SSL greeting to client.");
			update_stats(STAT_BADCONN);
			destroy_conn(connptr);
			return;
		}
	}

	relay_connection(connptr);

	log_message(LOG_INFO, "Closed connection between local client (fd:%d) and remote client (fd:%d)",
		    connptr->client_fd, connptr->server_fd);

	/*
	 * All done... close everything and go home... :)
	 */
	destroy_conn(connptr);
	return;
}
Beispiel #3
0
/*
 * Establish a connection to the upstream proxy server.
 */
static int
connect_to_upstream(struct conn_s *connptr, struct request_s *request)
{
#ifndef UPSTREAM_SUPPORT
	/*
	 * This function does nothing if upstream support was not compiled
	 * into tinyproxy.
	 */
	return -1;
#else
	char *combined_string;
	int len;

	struct upstream *cur_upstream = connptr->upstream_proxy;
	if(!cur_upstream) {
		log_message(LOG_WARNING,
			    "No upstream proxy defined for %s.",
			    request->host);
		indicate_http_error(connptr, 404, "Unable to connect to upstream proxy.");
		return -1;
	}

	connptr->server_fd =
	    opensock(cur_upstream->host, cur_upstream->port);

	if (connptr->server_fd < 0) {
		log_message(LOG_WARNING,
			    "Could not connect to upstream proxy.");
		indicate_http_error(connptr, 404, "Unable to connect to upstream proxy",
				    "detail", "A network error occurred while trying to connect to the upstream web proxy.",
				    NULL);
		return -1;
	}

	log_message(LOG_CONN,
		    "Established connection to upstream proxy \"%s\" using file descriptor %d.",
		    cur_upstream->host, connptr->server_fd);

	/*
	 * We need to re-write the "path" part of the request so that we
	 * can reuse the establish_http_connection() function. It expects a
	 * method and path.
	 */
	if (connptr->connect_method) {
		len = strlen(request->host) + 7;

		combined_string = safemalloc(len);
		if (!combined_string) {
			return -1;
		}

		snprintf(combined_string, len, "%s:%d", request->host,
			 request->port);
	} else {
		len = strlen(request->host) + strlen(request->path) + 14;
		combined_string = safemalloc(len);
		if (!combined_string) {
			return -1;
		}

		snprintf(combined_string, len, "http://%s:%d%s", request->host,
			 request->port, request->path);
	}

	if (request->path)
		safefree(request->path);
	request->path = combined_string;

	return establish_http_connection(connptr, request);
#endif
}
Beispiel #4
0
/*
 * Loop through all the headers (including the response code) from the
 * server.
 */
static int
process_server_headers(struct conn_s *connptr)
{
	static char *skipheaders[] = {
		"keep-alive",
		"proxy-authenticate",
		"proxy-authorization",
		"proxy-connection",
		"transfer-encoding",
	};

	char *response_line;

	hashmap_t hashofheaders;
	hashmap_iter iter;
	char *data, *header;
	ssize_t len;
	int i;
	int ret;

	/* FIXME: Remember to handle a "simple_req" type */

	/* Get the response line from the remote server. */
      retry:
	len = readline(connptr->server_fd, &response_line);
	if (len <= 0)
		return -1;

	/*
	 * Strip the new line and character return from the string.
	 */
	if (chomp(response_line, len) == len) {
		/*
		 * If the number of characters removed is the same as the
		 * length then it was a blank line. Free the buffer and
		 * try again (since we're looking for a request line.)
		 */
		safefree(response_line);
		goto retry;
	}

	hashofheaders = hashmap_create(HEADER_BUCKETS);
	if (!hashofheaders) {
		safefree(response_line);
		return -1;
	}

	/*
	 * Get all the headers from the remote server in a big hash
	 */
	if (get_all_headers(connptr->server_fd, hashofheaders) < 0) {
		log_message(LOG_WARNING, "Could not retrieve all the headers from the remote server.");
		hashmap_delete(hashofheaders);
		safefree(response_line);

		indicate_http_error(connptr, 503, "Could not retrieve all the headers",
				    "detail", PACKAGE " was unable to retrieve and process headers from the remote web server.",
				    NULL);
		return -1;
	}

	/* Send the saved response line first */
	ret = write_message(connptr->client_fd, "%s\r\n", response_line);
	safefree(response_line);
	if (ret < 0)
		goto ERROR_EXIT;

	/*
	 * If there is a "Content-Length" header, retrieve the information
	 * from it for later use.
	 */
	connptr->content_length.server = get_content_length(hashofheaders);

	/*
	 * See if there is a connection header.  If so, we need to to a bit of
	 * processing.
	 */
	remove_connection_headers(hashofheaders);

	/*
	 * Delete the headers listed in the skipheaders list
	 */
	for (i = 0; i != (sizeof(skipheaders) / sizeof(char *)); i++) {
		hashmap_remove(hashofheaders, skipheaders[i]);
	}

	/* Send, or add the Via header */
	ret = write_via_header(connptr->client_fd, hashofheaders,
			       connptr->protocol.major,
			       connptr->protocol.minor);
	if (ret < 0)
		goto ERROR_EXIT;

	/*
	 * All right, output all the remaining headers to the client.
	 */
	iter = hashmap_first(hashofheaders);
	if (iter >= 0) {
		for ( ; !hashmap_is_end(hashofheaders, iter); ++iter) {
			hashmap_return_entry(hashofheaders,
					     iter,
					     &data,
					     (void **)&header);

			ret = write_message(connptr->client_fd,
						  "%s: %s\r\n",
						  data, header);
			if (ret < 0)
				goto ERROR_EXIT;
		}
	}
	hashmap_delete(hashofheaders);

	/* Write the final blank line to signify the end of the headers */
	if (safe_write(connptr->client_fd, "\r\n", 2) < 0)
		return -1;

	return 0;

  ERROR_EXIT:
	hashmap_delete(hashofheaders);
	return -1;
}
Beispiel #5
0
/*
 * Here we loop through all the headers the client is sending. If we
 * are running in anonymous mode, we will _only_ send the headers listed
 * (plus a few which are required for various methods).
 *	- rjkaes
 */
static int
process_client_headers(struct conn_s *connptr, hashmap_t hashofheaders)
{
	static char *skipheaders[] = {
		"host",
		"keep-alive",
		"proxy-authenticate",
		"proxy-authorization",
		"proxy-connection",
		"te",
		"trailers",
		"transfer-encoding",
		"upgrade"
	};
	int i;
	hashmap_iter iter;
	int ret = 0;

	char *data, *header;

	/*
	 * Don't send headers if there's already an error, if the request was
	 * a stats request, or if this was a CONNECT method (unless upstream
	 * proxy is in use.)
	 */
	if (connptr->server_fd == -1 || connptr->show_stats
	    || (connptr->connect_method && (connptr->upstream_proxy == NULL))) {
		log_message(LOG_INFO, "Not sending client headers to remote machine");
		return 0;
	}

	/*
	 * See if there is a "Content-Length" header.  If so, again we need
	 * to do a bit of processing.
	 */
	connptr->content_length.client = get_content_length(hashofheaders);

	/*
	 * See if there is a "Connection" header.  If so, we need to do a bit
	 * of processing. :)
	 */
	remove_connection_headers(hashofheaders);

	/*
	 * Delete the headers listed in the skipheaders list
	 */
	for (i = 0; i != (sizeof(skipheaders) / sizeof(char *)); i++) {
		hashmap_remove(hashofheaders, skipheaders[i]);
	}

	/* Send, or add the Via header */
	ret = write_via_header(connptr->server_fd, hashofheaders,
			       connptr->protocol.major,
			       connptr->protocol.minor);
	if (ret < 0) {
		indicate_http_error(connptr, 503,
				    "Could not send data to remote server",
				    "detail", "A network error occurred while trying to write data to the remote web server.",
				    NULL);
		goto PULL_CLIENT_DATA;
	}

	/*
	 * Output all the remaining headers to the remote machine.
	 */
	iter = hashmap_first(hashofheaders);
	if (iter >= 0) {
		for ( ; !hashmap_is_end(hashofheaders, iter); ++iter) {
			hashmap_return_entry(hashofheaders,
					     iter,
					     &data,
					     (void**)&header);

			if (!is_anonymous_enabled() || anonymous_search(data) > 0) {
				ret = write_message(connptr->server_fd,
						    "%s: %s\r\n",
						    data, header);
				if (ret < 0) {
					indicate_http_error(connptr, 503,
							    "Could not send data to remote server",
							    "detail", "A network error occurred while trying to write data to the remote web server.",
							    NULL);
					goto PULL_CLIENT_DATA;
				}
			}
		}
	}

#if defined(XTINYPROXY_ENABLE)
	if (config.my_domain)
		add_xtinyproxy_header(connptr);
#endif
	
	/* Write the final "blank" line to signify the end of the headers */
	if (safe_write(connptr->server_fd, "\r\n", 2) < 0)
		return -1;

	/*
	 * Spin here pulling the data from the client.
	 */
  PULL_CLIENT_DATA:
	if (connptr->content_length.client > 0)
		return pull_client_data(connptr,
					connptr->content_length.client);
	else
		return ret;
}
Beispiel #6
0
int
do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders,
                      struct request_s *request, struct config_s *conf,
                      char **url)
{
        socklen_t length;
        char *data;
        size_t ulen = strlen (*url);
        ssize_t i;

        length = hashmap_entry_by_key (hashofheaders, "host", (void **) &data);
        if (length <= 0) {
                struct sockaddr_in dest_addr;

                if (getsockname
                    (connptr->client_fd, (struct sockaddr *) &dest_addr,
                     &length) < 0) {
                        log_message (LOG_ERR,
                                     "process_request: cannot get destination IP for %d",
                                     connptr->client_fd);
                        indicate_http_error (connptr, 400, "Bad Request",
                                             "detail", "Unknown destination",
                                             "url", *url, NULL);
                        return 0;
                }

                request->host = (char *) safemalloc (17);
                strlcpy (request->host, inet_ntoa (dest_addr.sin_addr), 17);

                request->port = ntohs (dest_addr.sin_port);

                request->path = (char *) safemalloc (ulen + 1);
                strlcpy (request->path, *url, ulen + 1);

                build_url (url, request->host, request->port, request->path);
                log_message (LOG_INFO,
                             "process_request: trans IP %s %s for %d",
                             request->method, *url, connptr->client_fd);
        } else {
                request->host = (char *) safemalloc (length + 1);
                if (sscanf (data, "%[^:]:%hu", request->host, &request->port) !=
                    2) {
                        strlcpy (request->host, data, length + 1);
                        request->port = HTTP_PORT;
                }

                request->path = (char *) safemalloc (ulen + 1);
                strlcpy (request->path, *url, ulen + 1);

                build_url (url, request->host, request->port, request->path);
                log_message (LOG_INFO,
                             "process_request: trans Host %s %s for %d",
                             request->method, *url, connptr->client_fd);
        }

        if (conf->listen_addrs == NULL) {
                return 1;
        }

        for (i = 0; i < vector_length(conf->listen_addrs); i++) {
                const char *addr;

                addr = (char *)vector_getentry(conf->listen_addrs, i, NULL);

                if (addr && strcmp(request->host, addr) == 0) {
                        log_message(LOG_ERR,
                                    "transparent: destination IP %s is local "
                                    "on socket fd %d",
                                    request->host, connptr->client_fd);
                        indicate_http_error(connptr, 400, "Bad Request",
                                            "detail",
                                            "You tried to connect to the "
                                            "machine the proxy is running on",
                                            "url", *url, NULL);
                        return 0;
                }
        }

        return 1;
}