#include <netdb.h>
/* poll() */
#include <poll.h>
+/* errno */
+#include <errno.h>
+/* va_*() */
+#include <stdarg.h>
+/* pthread_*() */
+#include <pthread.h>
/* Maximum line of a HTTP request line. Longer request lines are aborted with
* should be a good limit to make processing simpler. */
#define MAX_REQUEST_LINE 4096
+/* Helper macro for LOG/LOG_PERROR. Print file/line number if compiled with
+ * debug output. */
+#ifdef DEBUG
+#define LOG_PRINT_LOCATION fprintf(stdout, "%s:%-3d ", __FILE__, __LINE__);
+#else
+#define LOG_PRINT_LOCATION
+#endif
+/* Call log_message() and print current file and line number. */
+#define LOG \
+ LOG_PRINT_LOCATION \
+ log_message
+/* perror() replacement with debug level support. */
+#define LOG_PERROR(level, message) \
+ LOG_PRINT_LOCATION \
+ log_message(level, "%s: %s", message, strerror(errno))
+
static int read_http_request(FILE *client_fd, char *request, size_t length);
static void send_close_bad_request(FILE *client_fd);
static int parse_request(const char *buffer, char *host, char *port,
int *version_minor);
+static void log_message(int level, const char *format, ...);
+
void handle_connection(int client_socket) {
int server_socket;
int version_minor;
int result;
+ LOG(LOG_DEBUG, "new connection");
+
client_fd = fdopen(client_socket, "a+");
if (NULL == client_fd) {
- perror("fdopen()");
+ LOG_PERROR(LOG_WARNING, "fdopen(): client failed");
close(client_socket);
return;
}
-#ifdef DEBUG
- printf("New connection:\n");
-#endif
-
/* Read request line (CONNECT ..) and headers (they are discarded). */
result = read_http_request(client_fd, buffer, sizeof(buffer));
if (-1 == result) {
/* Read error, client_fd already closed. */
+ LOG(LOG_WARNING, "read_http_request(): client read error");
return;
} else if (-2 == result) {
/* EOF */
+ LOG(LOG_WARNING, "read_http_request(): client EOF");
send_close_bad_request(client_fd);
return;
}
-#ifdef DEBUG
- printf(" request: %s", buffer);
-#endif
-
if (0 != parse_request(buffer, host, port, &version_minor)) {
+ LOG(LOG_WARNING, "bad request: %s", buffer);
send_close_bad_request(client_fd);
-#ifdef DEBUG
- printf(" bad request\n");
-#endif
return;
}
-#ifdef DEBUG
- printf(" %s:%s (HTTP 1.%d)\n", host, port, version_minor);
-#endif
+ LOG(LOG_DEBUG, "target: %s:%s (HTTP 1.%d)", host, port, version_minor);
/* Connect to proxy server or directly to server. */
if (NULL != global_proxy_host && NULL != global_proxy_port) {
+ LOG(LOG_DEBUG, "connecting to %s:%s", global_proxy_host,
+ global_proxy_port);
server_socket = connect_to_host(global_proxy_host, global_proxy_port);
} else {
+ LOG(LOG_DEBUG, "connecting to %s:%s", host, port);
server_socket = connect_to_host(host, port);
}
if (-1 == server_socket) {
+ LOG(LOG_WARNING, "failed to connect to server");
send_close_forwarding_failure(client_fd);
return;
}
server_fd = fdopen(server_socket, "a+");
if (NULL == server_fd) {
+ LOG_PERROR(LOG_WARNING, "fdopen(): server failed");
send_close_forwarding_failure(client_fd);
close(server_socket);
return;
result = read_http_request(server_fd, buffer, sizeof(buffer));
if (-1 == result) {
/* Read error, server_fd already closed. */
+ LOG(LOG_WARNING, "read_http_request(): proxy read error");
send_close_forwarding_failure(client_fd);
return;
} else if (-2 == result) {
/* EOF */
+ LOG(LOG_WARNING, "read_http_request(): proxy EOF");
fclose(server_fd);
send_close_forwarding_failure(client_fd);
return;
/* Check response of proxy server. */
if (0 != strncmp(buffer, "HTTP/1.0 200", 12)) {
-#ifdef DEBUG
- printf(" bad proxy response\n");
-#endif
+ LOG(LOG_WARNING, "bad proxy response: %s", buffer);
fclose(server_fd);
send_close_forwarding_failure(client_fd);
return;
}
}
-#ifdef DEBUG
- printf(" connection to server established\n");
-#endif
+ LOG(LOG_DEBUG, "connection to server established");
/* We've established a connection, tell the client. */
fprintf(client_fd, "HTTP/1.0 200 Connection established\r\n");
fclose(client_fd);
fclose(server_fd);
+
+ LOG(LOG_DEBUG, "connection to server closed");
+
+ LOG(LOG_DEBUG, "connection finished");
}
/* Read HTTP request line and headers (ignored).
if (NULL == fgets(request, (int)length, client_fd)) {
if (ferror(client_fd)) {
- perror("fgets(), request");
+ LOG_PERROR(LOG_WARNING, "read_http_request(): fgets()");
fclose(client_fd);
return -1;
}
}
}
if (ferror(client_fd)) {
- perror("fgets(), header");
+ LOG_PERROR(LOG_WARNING, "read_http_request(): fgets()");
fclose(client_fd);
return -1;
}
for (;;) {
int result = poll(fds, 2, -1 /* no timeout */);
if (result < 0) {
- perror("poll()");
+ LOG_PERROR(LOG_ERROR, "transfer_data(): poll()");
return;
}
size_read = read(from, buffer, sizeof(buffer));
if (0 > size_read) {
- perror("read()");
+ LOG_PERROR(LOG_WARNING, "read_from_write_to(): read()");
return -1;
}
/* EOF */
size_written = write(to, buffer, (size_t)size_read);
if (0 > size_written) {
- perror("write()");
+ LOG_PERROR(LOG_WARNING, "read_from_write_to(): write()");
return -1;
}
if (size_read != size_written) {
- printf("only written %ld of %ld bytes!\n", (long int)size_written,
- (long int)size_read);
+ LOG(LOG_ERROR, "read_from_write_to(): only written %ld of %ld bytes!",
+ (long int)size_written, (long int)size_read);
return -1;
}
| AI_V4MAPPED; /* support IPv4 through IPv6 */
gai_return = getaddrinfo(hostname, port, &gai_hints, &gai_result);
if (0 != gai_return) {
- perror("getaddrinfo()");
+ LOG_PERROR(LOG_WARNING, "connect_to_host(): getaddrinfo()");
return -1;
}
server->ai_socktype,
server->ai_protocol);
if (-1 == server_socket) {
- perror("socket(), trying next");
+ LOG_PERROR(LOG_DEBUG, "connect_to_host(): socket(), trying next");
continue;
}
server->ai_addrlen)) {
break;
}
- perror("connect(), trying next");
+ LOG_PERROR(LOG_DEBUG, "connect_to_host(): connect(), trying next");
close(server_socket);
}
freeaddrinfo(gai_result);
if (NULL == server) {
- fprintf(stderr, "no server found, aborting\n");
+ LOG_PERROR(LOG_WARNING, "connect_to_host(): no server found, abort");
return -1;
}
return 0;
}
+
+
+static void log_message(int level, const char *format, ...) {
+ va_list ap;
+ const char *level_string;
+
+ if (global_log_level < level) {
+ return;
+ }
+
+ switch (level) {
+ case LOG_ERROR: level_string = "ERROR"; break;
+ case LOG_WARNING: level_string = "WARNING"; break;
+ case LOG_DEBUG: level_string = "DEBUG"; break;
+ default: level_string = "UNKNOWN";
+ }
+
+ va_start(ap, format);
+ fprintf(stdout, "[%s] [%d] ", level_string, (int)pthread_self());
+ vfprintf(stdout, format, ap);
+ fprintf(stdout, "\n");
+ va_end(ap);
+}
return EXIT_FAILURE;
}
-#ifdef DEBUG
- printf("Listening for connections on port %d.\n", port);
+ if (LOG_DEBUG <= global_log_level) {
+ printf("Listening for connections on port %d.\n", port);
- if (NULL != global_proxy_host && NULL != global_proxy_port) {
- printf("Using proxy: %s:%s.\n", global_proxy_host, global_proxy_port);
+ if (NULL != global_proxy_host && NULL != global_proxy_port) {
+ printf("Using proxy: %s:%s.\n", global_proxy_host,
+ global_proxy_port);
+ }
}
-#endif
while (!done) {
/* Accept new connection. */
/* Default values. */
thread_count = 10;
+#ifdef DEBUG
+ global_log_level = LOG_DEBUG;
+#else
+ global_log_level = LOG_WARNING;
+#endif
- while (-1 != (option = getopt(argc, argv, "p:t:h?"))) {
+ while (-1 != (option = getopt(argc, argv, "d:p:t:h?"))) {
switch (option) {
+ case 'd': {
+ if (0 > atoi(optarg)) {
+ print_usage(argv[0]);
+ fprintf(stderr, "\n-d positive number required\n");
+ exit(EXIT_FAILURE);
+ }
+ global_log_level = atoi(optarg);
+ break;
+ }
case 'p': {
char *position;
}
}
static void print_usage(const char *argv) {
- fprintf(stderr, "Usage: %s [-p host:port] [-t count] port\n", argv);
+ fprintf(stderr, "Usage: %s [-d level] [-p host:port] [-t count] port\n",
+ argv);
fprintf(stderr, "\n");
+ fprintf(stderr, "-d debug level: 0=errors only, 2=debug [default: 1]\n");
fprintf(stderr, "-p proxy hostname and port\n");
fprintf(stderr, "-t number of threads [default: 10]\n");
}