From 3b3ccb0c2ec73f0b7388319f6b0616e12642daf9 Mon Sep 17 00:00:00 2001 From: andronoob Date: Tue, 20 Apr 2021 09:32:20 +0000 Subject: [PATCH 01/11] parse sni hostname (copied from @aviramc); updated tls.c to latest version --- Makefile | 2 +- http-connect.c | 18 +- logger.c | 501 ++++++++++++++++++++++++++++++++++++++++++ logger.h | 66 ++++++ protocol.h | 39 ++++ redsocks.c | 140 +++++++++++- redsocks.conf.example | 4 + redsocks.h | 2 + tls.c | 251 +++++++++++++++++++++ tls.h | 37 ++++ 10 files changed, 1055 insertions(+), 5 deletions(-) create mode 100644 logger.c create mode 100644 logger.h create mode 100644 protocol.h create mode 100644 tls.c create mode 100644 tls.h diff --git a/Makefile b/Makefile index 5ab12cba..00e9d33a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -include make.conf -OBJS := parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o dnsu2t.o gen/version.o +OBJS := parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o dnsu2t.o tls.o logger.o gen/version.o ifeq ($(DBG_BUILD),1) OBJS += debug.o endif diff --git a/http-connect.c b/http-connect.c index f7679aca..26976422 100644 --- a/http-connect.c +++ b/http-connect.c @@ -43,6 +43,10 @@ typedef enum httpc_state_t { #define HTTP_HEAD_WM_HIGH 4096 // that should be enough for one HTTP line. +#define MAX_SERVER_NAME (253) /* Max DNS is 253 characters */ +#define MAX_PORT_STR_LENGTH (6) /* Ports are 5 digits decimax max */ +#define MAX_CONNECT_HOST_LENGTH (MAX_SERVER_NAME + MAX_PORT_STR_LENGTH + 1) /* Add one byte for \0 */ + static void httpc_client_init(redsocks_client *client) { @@ -210,6 +214,14 @@ static struct evbuffer *httpc_mkconnect(redsocks_client *client) struct evbuffer *buff = NULL, *retval = NULL; char *auth_string = NULL; int len; + char *hostname = NULL; + + + if (client->hostname) { + hostname = client->hostname; + } else { + hostname = inet_ntoa(client->destaddr.sin_addr); + } buff = evbuffer_new(); if (!buff) { @@ -230,8 +242,8 @@ static struct evbuffer *httpc_mkconnect(redsocks_client *client) auth_scheme = "Basic"; } else if (strncasecmp(auth->last_auth_query, "Digest", 6) == 0) { /* calculate uri */ - char uri[128]; - snprintf(uri, 128, "%s:%u", inet_ntoa(client->destaddr.sin_addr), ntohs(client->destaddr.sin_port)); + char uri[MAX_CONNECT_HOST_LENGTH] = {0}; + snprintf(uri, MAX_CONNECT_HOST_LENGTH, "%s:%u", hostname, ntohs(client->destaddr.sin_port)); /* prepare an random string for cnounce */ char cnounce[17]; @@ -246,7 +258,7 @@ static struct evbuffer *httpc_mkconnect(redsocks_client *client) // TODO: do accurate evbuffer_expand() while cleaning up http-auth len = evbuffer_add_printf(buff, "CONNECT %s:%u HTTP/1.0\r\n", - inet_ntoa(client->destaddr.sin_addr), + hostname, ntohs(client->destaddr.sin_port)); if (len < 0) { redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf"); diff --git a/logger.c b/logger.c new file mode 100644 index 00000000..26aad8ca --- /dev/null +++ b/logger.c @@ -0,0 +1,501 @@ +/* + * Copyright (c) 2013, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "logger.h" + +struct Logger { + struct LogSink *sink; + int priority; + int facility; + int reference_count; +}; + +struct LogSink { + enum { + LOG_SINK_SYSLOG, + LOG_SINK_STDERR, + LOG_SINK_FILE + } type; + const char *filepath; + + FILE *fd; + int reference_count; + SLIST_ENTRY(LogSink) entries; +}; + + +static struct Logger *default_logger = NULL; +static SLIST_HEAD(LogSink_head, LogSink) sinks = SLIST_HEAD_INITIALIZER(sinks); + + +static void free_logger(struct Logger *); +static void init_default_logger(); +static void vlog_msg(struct Logger *, int, const char *, va_list); +static void free_at_exit(); +static int lookup_syslog_facility(const char *); +static const char *timestamp(char *, size_t); +static struct LogSink *obtain_stderr_sink(); +static struct LogSink *obtain_syslog_sink(); +static struct LogSink *obtain_file_sink(const char *); +static struct LogSink *log_sink_ref_get(struct LogSink *); +static void log_sink_ref_put(struct LogSink *); +static void free_sink(struct LogSink *); + + +struct Logger * +new_syslog_logger(const char *facility) { + struct Logger *logger = malloc(sizeof(struct Logger)); + if (logger != NULL) { + logger->sink = obtain_syslog_sink(); + if (logger->sink == NULL) { + free(logger); + return NULL; + } + logger->priority = LOG_DEBUG; + logger->facility = lookup_syslog_facility(facility); + logger->reference_count = 0; + + log_sink_ref_get(logger->sink); + } + + return logger; +} + +struct Logger * +new_file_logger(const char *filepath) { + struct Logger *logger = malloc(sizeof(struct Logger)); + if (logger != NULL) { + logger->sink = obtain_file_sink(filepath); + if (logger->sink == NULL) { + free(logger); + return NULL; + } + logger->priority = LOG_DEBUG; + logger->facility = 0; + logger->reference_count = 0; + + log_sink_ref_get(logger->sink); + } + + return logger; +} + +void +reopen_loggers() { + struct LogSink *sink; + + SLIST_FOREACH(sink, &sinks, entries) { + if (sink->type == LOG_SINK_SYSLOG) { + closelog(); + openlog("redsocks", LOG_PID, 0); + } else if (sink->type == LOG_SINK_FILE) { + sink->fd = freopen(sink->filepath, "a", sink->fd); + if (sink->fd == NULL) + err("failed to reopen log file %s: %s", + sink->filepath, strerror(errno)); + else + setvbuf(sink->fd, NULL, _IOLBF, 0); + } + } +} + +void +set_default_logger(struct Logger *new_logger) { + struct Logger *old_default_logger = default_logger; + + assert(new_logger != NULL); + default_logger = logger_ref_get(new_logger); + logger_ref_put(old_default_logger); +} + +void +set_logger_priority(struct Logger *logger, int priority) { + assert(logger != NULL); + assert(priority >= LOG_EMERG && priority <= LOG_DEBUG); + logger->priority = priority; +} + +void +logger_ref_put(struct Logger *logger) { + if (logger == NULL) + return; + + assert(logger->reference_count > 0); + logger->reference_count--; + if (logger->reference_count == 0) + free_logger(logger); +} + +struct Logger * +logger_ref_get(struct Logger *logger) { + if (logger != NULL) + logger->reference_count++; + + return logger; +} + +static void +free_logger(struct Logger *logger) { + if (logger == NULL) + return; + + log_sink_ref_put(logger->sink); + logger->sink = NULL; + + free(logger); +} + +void +log_msg(struct Logger *logger, int priority, const char *format, ...) { + va_list args; + + va_start(args, format); + vlog_msg(logger, priority, format, args); + va_end(args); +} + +void +fatal(const char *format, ...) { + va_list args; + + init_default_logger(); + + va_start(args, format); + vlog_msg(default_logger, LOG_CRIT, format, args); + va_end(args); + + exit(EXIT_FAILURE); +} + +void +err(const char *format, ...) { + va_list args; + + init_default_logger(); + + va_start(args, format); + vlog_msg(default_logger, LOG_ERR, format, args); + va_end(args); +} + +void +warn(const char *format, ...) { + va_list args; + + init_default_logger(); + + va_start(args, format); + vlog_msg(default_logger, LOG_WARNING, format, args); + va_end(args); +} + +void +notice(const char *format, ...) { + va_list args; + + init_default_logger(); + + va_start(args, format); + vlog_msg(default_logger, LOG_NOTICE, format, args); + va_end(args); +} + +void +info(const char *format, ...) { + va_list args; + + init_default_logger(); + + va_start(args, format); + vlog_msg(default_logger, LOG_INFO, format, args); + va_end(args); +} + +void +debug(const char *format, ...) { + va_list args; + + init_default_logger(); + + va_start(args, format); + vlog_msg(default_logger, LOG_DEBUG, format, args); + va_end(args); +} + +static void +vlog_msg(struct Logger *logger, int priority, const char *format, va_list args) { + assert(logger != NULL); + + if (priority > logger->priority) + return; + + if (logger->sink->type == LOG_SINK_SYSLOG) { + vsyslog(logger->facility|logger->priority, format, args); + } else if (logger->sink->fd != NULL) { + char buffer[1024]; + + timestamp(buffer, sizeof(buffer)); + size_t len = strlen(buffer); + + vsnprintf(buffer + len, sizeof(buffer) - len, format, args); + buffer[sizeof(buffer) - 1] = '\0'; /* ensure buffer null terminated */ + + fprintf(logger->sink->fd, "%s\n", buffer); + } +} + +static void +init_default_logger() { + struct Logger *logger = NULL; + + if (default_logger != NULL) + return; + + logger = malloc(sizeof(struct Logger)); + if (logger != NULL) { + logger->sink = obtain_stderr_sink(); + if (logger->sink == NULL) { + free(logger); + return; + } + logger->priority = LOG_DEBUG; + logger->facility = 0; + logger->reference_count = 0; + + log_sink_ref_get(logger->sink); + } + + if (logger == NULL) + return; + + atexit(free_at_exit); + + default_logger = logger_ref_get(logger); +} + +static void +free_at_exit() { + logger_ref_put(default_logger); + default_logger = NULL; +} + +static int +lookup_syslog_facility(const char *facility) { + static const struct { + const char *name; + int number; + } facilities[] = { + { "auth", LOG_AUTH }, + { "cron", LOG_CRON }, + { "daemon", LOG_DAEMON }, + { "ftp", LOG_FTP }, + { "local0", LOG_LOCAL0 }, + { "local1", LOG_LOCAL1 }, + { "local2", LOG_LOCAL2 }, + { "local3", LOG_LOCAL3 }, + { "local4", LOG_LOCAL4 }, + { "local5", LOG_LOCAL5 }, + { "local6", LOG_LOCAL6 }, + { "local7", LOG_LOCAL7 }, + { "mail", LOG_MAIL }, + { "news", LOG_NEWS }, + { "user", LOG_USER }, + { "uucp", LOG_UUCP }, + }; + + for (size_t i = 0; i < sizeof(facilities) / sizeof(facilities[0]); i++) + if (strncasecmp(facilities[i].name, facility, strlen(facility)) == 0) + return facilities[i].number; + + /* fall back value */ + return LOG_USER; +} + +static struct LogSink * +obtain_stderr_sink() { + struct LogSink *sink; + + SLIST_FOREACH(sink, &sinks, entries) { + if (sink->type == LOG_SINK_STDERR) + return sink; + } + + sink = malloc(sizeof(struct LogSink)); + if (sink != NULL) { + sink->type = LOG_SINK_STDERR; + sink->filepath = NULL; + sink->fd = stderr; + sink->reference_count = 0; + + SLIST_INSERT_HEAD(&sinks, sink, entries); + } + + return sink; +} + +static struct LogSink * +obtain_syslog_sink() { + struct LogSink *sink; + + SLIST_FOREACH(sink, &sinks, entries) { + if (sink->type == LOG_SINK_SYSLOG) + return sink; + } + + sink = malloc(sizeof(struct LogSink)); + if (sink != NULL) { + sink->type = LOG_SINK_SYSLOG; + sink->filepath = NULL; + sink->fd = NULL; + sink->reference_count = 0; + + openlog("redsocks", LOG_PID, 0); + + SLIST_INSERT_HEAD(&sinks, sink, entries); + } + + return sink; +} + +static struct LogSink * +obtain_file_sink(const char *filepath) { + struct LogSink *sink; + + if (filepath == NULL) + return NULL; + + SLIST_FOREACH(sink, &sinks, entries) { + if (sink->type == LOG_SINK_FILE && + strcmp(sink->filepath, filepath) == 0) + return sink; + } + + sink = malloc(sizeof(struct LogSink)); + if (sink == NULL) + return NULL; + + + FILE *fd = fopen(filepath, "a"); + if (fd == NULL) { + free(sink); + err("Failed to open new log file: %s", filepath); + return NULL; + } + setvbuf(fd, NULL, _IOLBF, 0); + + sink->type = LOG_SINK_FILE; + sink->filepath = strdup(filepath); + sink->fd = fd; + sink->reference_count = 0; + + SLIST_INSERT_HEAD(&sinks, sink, entries); + + return sink; +} + +static struct LogSink * +log_sink_ref_get(struct LogSink *sink) { + if (sink != NULL) + sink->reference_count++; + + return sink; +} + +static void +log_sink_ref_put(struct LogSink *sink) { + if (sink == NULL) + return; + + assert(sink->reference_count > 0); + sink->reference_count--; + if (sink->reference_count == 0) + free_sink(sink); +} + +static void +free_sink(struct LogSink *sink) { + if (sink == NULL) + return; + + SLIST_REMOVE(&sinks, sink, LogSink, entries); + + switch(sink->type) { + case LOG_SINK_SYSLOG: + closelog(); + break; + case LOG_SINK_STDERR: + fflush(sink->fd); + sink->fd = NULL; + break; + case LOG_SINK_FILE: + fclose(sink->fd); + sink->fd = NULL; + free((char *)sink->filepath); + sink->filepath = NULL; + break; + default: + assert(0); + } + + free(sink); +} + +static const char * +timestamp(char *dst, size_t dst_len) { + /* TODO change to ev_now() */ + time_t now = time(NULL); + static struct { + time_t when; + char string[32]; + } timestamp_cache = { .when = 0, .string = {'\0'} }; + + if (now != timestamp_cache.when) { +#ifdef RFC3339_TIMESTAMP + struct tm *tmp = gmtime(&now); + strftime(timestamp_cache.string, sizeof(timestamp_cache.string), + "%FT%TZ ", tmp); +#else + struct tm *tmp = localtime(&now); + strftime(timestamp_cache.string, sizeof(timestamp_cache.string), + "%F %T ", tmp); +#endif + + timestamp_cache.when = now; + } + + if (dst != NULL) + strncpy(dst, timestamp_cache.string, dst_len); + + return timestamp_cache.string; +} diff --git a/logger.h b/logger.h new file mode 100644 index 00000000..657e8216 --- /dev/null +++ b/logger.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef LOGGER_H +#define LOGGER_H + +struct Logger; + +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 + +struct Logger *new_syslog_logger(const char *facility); +struct Logger *new_file_logger(const char *filepath); +void set_default_logger(struct Logger *); +void set_logger_priority(struct Logger *, int); +struct Logger *logger_ref_get(struct Logger *); +void logger_ref_put(struct Logger *); +void reopen_loggers(); + +/* Shorthand to log to global error log */ +void fatal(const char *, ...) + __attribute__ ((format (printf, 1, 2))) + __attribute__ ((noreturn)); +void err(const char *, ...) + __attribute__ ((format (printf, 1, 2))); +void warn(const char *, ...) + __attribute__ ((format (printf, 1, 2))); +void notice(const char *, ...) + __attribute__ ((format (printf, 1, 2))); +void info(const char *, ...) + __attribute__ ((format (printf, 1, 2))); +void debug(const char *, ...) + __attribute__ ((format (printf, 1, 2))); + +void log_msg(struct Logger *, int, const char *, ...) + __attribute__ ((format (printf, 3, 4))); + +#endif diff --git a/protocol.h b/protocol.h new file mode 100644 index 00000000..454c5fc0 --- /dev/null +++ b/protocol.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PROTOCOL_H +#define PROTOCOL_H + +#include + +struct Protocol { + const char *const name; + const uint16_t default_port; + int (*const parse_packet)(const char*, size_t, char **); + const char *const abort_message; + const size_t abort_message_len; +}; + +#endif diff --git a/redsocks.c b/redsocks.c index 4ed9a0c0..762ef40c 100644 --- a/redsocks.c +++ b/redsocks.c @@ -37,7 +37,16 @@ #include "redsocks.h" #include "utils.h" #include "libevent-compat.h" +#include "tls.h" +#define MINIMUM_HOST_READ (10) +#define MAXIMUM_HOST_READ (0) + +typedef enum _redsocks_hostname_read_rc { + SUCCESS = 0, + DATA_MISSING = 1, + FATAL_ERROR = 2, +} redsocks_hostname_read_rc; #define REDSOCKS_RELAY_HALFBUFF 4096 @@ -81,6 +90,7 @@ static parser_entry redsocks_entries[] = { .key = "login", .type = pt_pchar }, { .key = "password", .type = pt_pchar }, { .key = "listenq", .type = pt_uint16 }, + { .key = "parse_sni_host", .type = pt_bool }, { .key = "splice", .type = pt_bool }, { .key = "disclose_src", .type = pt_disclose_src }, { .key = "on_proxy_fail", .type = pt_on_proxy_fail }, @@ -148,6 +158,7 @@ static int redsocks_onenter(parser_section *section) (strcmp(entry->key, "login") == 0) ? (void*)&instance->config.login : (strcmp(entry->key, "password") == 0) ? (void*)&instance->config.password : (strcmp(entry->key, "listenq") == 0) ? (void*)&instance->config.listenq : + (strcmp(entry->key, "parse_sni_host") == 0) ? (void*)&instance->config.parse_sni_host : (strcmp(entry->key, "splice") == 0) ? (void*)&instance->config.use_splice : (strcmp(entry->key, "disclose_src") == 0) ? (void*)&instance->config.disclose_src : (strcmp(entry->key, "on_proxy_fail") == 0) ? (void*)&instance->config.on_proxy_fail : @@ -735,6 +746,7 @@ void redsocks_drop_client(redsocks_client *client) } } redsocks_conn_list_del(client); + free(client->hostname); free(client); } @@ -867,6 +879,121 @@ int redsocks_read_expected(redsocks_client *client, struct evbuffer *input, void } } +static int redsocks_peek_buffer(redsocks_client *client, struct bufferevent *buffev, char **peek_buffer, size_t *peek_size) +{ + int n = 0, i = 0; + char *read_buffer = NULL; + size_t read_buffer_size = 0; + size_t read_buffer_position = 0; + struct evbuffer_iovec *v = NULL; + + n = evbuffer_peek(buffev->input, -1, NULL, NULL, 0); + + v = malloc(sizeof(struct evbuffer_iovec) * n); + if (NULL == v) { + redsocks_log_error(client, LOG_ERR, "malloc() error"); + goto finish; + } + + n = evbuffer_peek(buffev->input, -1, NULL, v, n); + + read_buffer_size = 0; + for (i = 0; i < n; i++) { + read_buffer_size += v[i].iov_len; + } + + read_buffer = (char *) malloc(read_buffer_size); + if (NULL == read_buffer) { + redsocks_log_error(client, LOG_ERR, "malloc() error"); + goto fail; + } + + read_buffer_position = 0; + for (i = 0; i < n; i++) { + memcpy(&read_buffer[read_buffer_position], v[i].iov_base, v[i].iov_len); + read_buffer_position += v[i].iov_len; + } + + fail: + free(v); + + finish: + *peek_buffer = read_buffer; + *peek_size = read_buffer_size; + + return 0; +} + +static redsocks_hostname_read_rc redsocks_read_sni(redsocks_client *client, char *read_buffer, size_t read_buffer_size, char **hostname) +{ + int rc = parse_tls_header(read_buffer, read_buffer_size, hostname); + + if (rc >= 0) { + return SUCCESS; + } + + /* rc < 0 */ + + if (rc != -4) { + return DATA_MISSING; + } + + /* rc == -4, malloc failure */ + return FATAL_ERROR; +} + +static void redsocks_hostname_reader(struct bufferevent *buffev, void *_arg) +{ + redsocks_client *client = _arg; + size_t read_buffer_size = 0; + char *read_buffer = NULL; + char *hostname = NULL; + redsocks_hostname_read_rc rc = FATAL_ERROR; + + assert(client->instance->config.parse_sni_host); + + if (!client->instance->config.parse_sni_host) { + return; + } + + if (0 != redsocks_peek_buffer(client, buffev, &read_buffer, &read_buffer_size)) { + redsocks_drop_client(client); + return; + } + + redsocks_log_error(client, LOG_INFO, "searching for hostname by sni"); + + if (client->instance->config.parse_sni_host) { + rc = redsocks_read_sni(client, read_buffer, read_buffer_size, &hostname); + } + + switch (rc) { + + case SUCCESS: + client->hostname = hostname; + redsocks_log_error(client, LOG_INFO, "found hostname %s, now connecting", client->hostname); + + if (client->instance->relay_ss->connect_relay) { + client->instance->relay_ss->connect_relay(client); + } else { + redsocks_connect_relay(client); + } + + break; + + case DATA_MISSING: + redsocks_log_error(client, LOG_INFO, "data is missing"); + + break; + + case FATAL_ERROR: /* passthourgh */ + default: + redsocks_drop_client(client); + } + + free(read_buffer); +} + struct evbuffer *mkevbuffer(void *data, size_t len) { struct evbuffer *buff = NULL, *retval = NULL; @@ -1225,7 +1352,11 @@ static void redsocks_accept_client(int fd, short what, void *_arg) redsocks_touch_client(client); - client->client = bufferevent_new(client_fd, NULL, NULL, redsocks_event_error, client); + if (!self->config.parse_sni_host) { + client->client = bufferevent_new(client_fd, NULL, NULL, redsocks_event_error, client); + } else { + client->client = bufferevent_new(client_fd, redsocks_hostname_reader, NULL, redsocks_event_error, client); + } if (!client->client) { log_errno(LOG_ERR, "bufferevent_new"); goto fail; @@ -1242,6 +1373,13 @@ static void redsocks_accept_client(int fd, short what, void *_arg) redsocks_log_error(client, LOG_INFO, "accepted"); + if (self->config.parse_sni_host) { + client->client->wm_read.low = MINIMUM_HOST_READ; + client->client->wm_read.high = MAXIMUM_HOST_READ; + /* We wait first for the client to give us the host */ + return; + } + if (self->relay_ss->connect_relay) self->relay_ss->connect_relay(client); else diff --git a/redsocks.conf.example b/redsocks.conf.example index 67bdf965..8224b256 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -112,6 +112,10 @@ redsocks { // close -- just close connection (default) // forward_http_err -- forward HTTP error page from proxy as-is // on_proxy_fail = close; + + // parse hostname from TLS SNI extension, + // so that HTTP CONNECT can use hostname rather than IP address + // parse_sni_host = true; } redudp { diff --git a/redsocks.h b/redsocks.h index b1aa01ec..9efe6b51 100644 --- a/redsocks.h +++ b/redsocks.h @@ -32,6 +32,7 @@ typedef struct redsocks_config_t { char *type; char *login; char *password; + bool parse_sni_host; uint16_t listenq; bool use_splice; enum disclose_src_e disclose_src; @@ -55,6 +56,7 @@ typedef struct redsocks_client_t { struct bufferevent *relay; struct sockaddr_in clientaddr; struct sockaddr_in destaddr; + char *hostname; int state; // it's used by bottom layer evshut_t client_evshut; evshut_t relay_evshut; diff --git a/tls.c b/tls.c new file mode 100644 index 00000000..744a3edd --- /dev/null +++ b/tls.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This is a minimal TLS implementation intended only to parse the server name + * extension. This was created based primarily on Wireshark dissection of a + * TLS handshake and RFC4366. + */ +#include +#include /* malloc() */ +#include +#include /* strncpy() */ +#include +#include +#include "tls.h" +#include "protocol.h" +#include "logger.h" + +#define SERVER_NAME_LEN 256 +#define TLS_HEADER_LEN 5 +#define TLS_HANDSHAKE_CONTENT_TYPE 0x16 +#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 + +#ifndef MIN +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#endif + + + +static const char tls_alert[] = { + 0x15, /* TLS Alert */ + 0x03, 0x01, /* TLS version */ + 0x00, 0x02, /* Payload length */ + 0x02, 0x28, /* Fatal, handshake failure */ +}; + +const struct Protocol *const tls_protocol = &(struct Protocol){ + .name = "tls", + .default_port = 443, + .parse_packet = (int (*const)(const char *, size_t, char **))&parse_tls_header, + .abort_message = tls_alert, + .abort_message_len = sizeof(tls_alert) +}; + + +/* Parse a TLS packet for the Server Name Indication extension in the client + * hello handshake, returning the first servername found (pointer to static + * array) + * + * Returns: + * >=0 - length of the hostname and updates *hostname + * caller is responsible for freeing *hostname + * -1 - Incomplete request + * -2 - No Host header included in this request + * -3 - Invalid hostname pointer + * -4 - malloc failure + * < -4 - Invalid TLS client hello + */ +int +parse_tls_header(const uint8_t *data, size_t data_len, char **hostname) { + uint8_t tls_content_type; + uint8_t tls_version_major; + uint8_t tls_version_minor; + size_t pos = TLS_HEADER_LEN; + size_t len; + + if (hostname == NULL) + return -3; + + /* Check that our TCP payload is at least large enough for a TLS header */ + if (data_len < TLS_HEADER_LEN) + return -1; + + /* SSL 2.0 compatible Client Hello + * + * High bit of first byte (length) and content type is Client Hello + * + * See RFC5246 Appendix E.2 + */ + if (data[0] & 0x80 && data[2] == 1) { + debug("Received SSL 2.0 Client Hello which can not support SNI."); + return -2; + } + + tls_content_type = data[0]; + if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) { + debug("Request did not begin with TLS handshake."); + return -5; + } + + tls_version_major = data[1]; + tls_version_minor = data[2]; + if (tls_version_major < 3) { + debug("Received SSL %" PRIu8 ".%" PRIu8 " handshake which can not support SNI.", + tls_version_major, tls_version_minor); + + return -2; + } + + /* TLS record length */ + len = ((size_t)data[3] << 8) + + (size_t)data[4] + TLS_HEADER_LEN; + data_len = MIN(data_len, len); + + /* Check we received entire TLS record length */ + if (data_len < len) + return -1; + + /* + * Handshake + */ + if (pos + 1 > data_len) { + return -5; + } + if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { + debug("Not a client hello"); + + return -5; + } + + /* Skip past fixed length records: + 1 Handshake Type + 3 Length + 2 Version (again) + 32 Random + to Session ID Length + */ + pos += 38; + + /* Session ID */ + if (pos + 1 > data_len) + return -5; + len = (size_t)data[pos]; + pos += 1 + len; + + /* Cipher Suites */ + if (pos + 2 > data_len) + return -5; + len = ((size_t)data[pos] << 8) + (size_t)data[pos + 1]; + pos += 2 + len; + + /* Compression Methods */ + if (pos + 1 > data_len) + return -5; + len = (size_t)data[pos]; + pos += 1 + len; + + if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) { + debug("Received SSL 3.0 handshake without extensions"); + return -2; + } + + /* Extensions */ + if (pos + 2 > data_len) + return -5; + len = ((size_t)data[pos] << 8) + (size_t)data[pos + 1]; + pos += 2; + + if (pos + len > data_len) + return -5; + return parse_extensions(data + pos, len, hostname); +} + +int +parse_extensions(const uint8_t *data, size_t data_len, char **hostname) { + size_t pos = 0; + size_t len; + + /* Parse each 4 bytes for the extension header */ + while (pos + 4 <= data_len) { + /* Extension Length */ + len = ((size_t)data[pos + 2] << 8) + + (size_t)data[pos + 3]; + + /* Check if it's a server name extension */ + if (data[pos] == 0x00 && data[pos + 1] == 0x00) { + /* There can be only one extension of each type, so we break + our state and move p to beinnging of the extension here */ + if (pos + 4 + len > data_len) + return -5; + return parse_server_name_extension(data + pos + 4, len, hostname); + } + pos += 4 + len; /* Advance to the next extension header */ + } + /* Check we ended where we expected to */ + if (pos != data_len) + return -5; + + return -2; +} + +int +parse_server_name_extension(const uint8_t *data, size_t data_len, + char **hostname) { + size_t pos = 2; /* skip server name list length */ + size_t len; + + while (pos + 3 < data_len) { + len = ((size_t)data[pos + 1] << 8) + + (size_t)data[pos + 2]; + + if (pos + 3 + len > data_len) + return -5; + + switch (data[pos]) { /* name type */ + case 0x00: /* host_name */ + *hostname = malloc(len + 1); + if (*hostname == NULL) { + err("malloc() failure"); + return -4; + } + + strncpy(*hostname, (const char *)(data + pos + 3), len); + + (*hostname)[len] = '\0'; + + return len; + default: + debug("Unknown server name extension name type: %" PRIu8, + data[pos]); + } + pos += 3 + len; + } + /* Check we ended where we expected to */ + if (pos != data_len) + return -5; + + return -2; +} diff --git a/tls.h b/tls.h new file mode 100644 index 00000000..52fdddff --- /dev/null +++ b/tls.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef TLS_H +#define TLS_H + +#include "protocol.h" + +extern const struct Protocol *const tls_protocol; + +int parse_tls_header(const uint8_t*, size_t, char **); +int parse_extensions(const uint8_t*, size_t, char **); +int parse_server_name_extension(const uint8_t*, size_t, char **); + +#endif From d2e5e7683e0e76d5ee3697cb05aa0e45c27c482c Mon Sep 17 00:00:00 2001 From: andronoob Date: Tue, 20 Apr 2021 22:18:18 +0800 Subject: [PATCH 02/11] allow connection in DATA_MISSING case --- redsocks.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/redsocks.c b/redsocks.c index 762ef40c..c1e2d21b 100644 --- a/redsocks.c +++ b/redsocks.c @@ -746,7 +746,8 @@ void redsocks_drop_client(redsocks_client *client) } } redsocks_conn_list_del(client); - free(client->hostname); + if (client->hostname != NULL) + free(client->hostname); free(client); } @@ -967,12 +968,15 @@ static void redsocks_hostname_reader(struct bufferevent *buffev, void *_arg) rc = redsocks_read_sni(client, read_buffer, read_buffer_size, &hostname); } + client->hostname = NULL; + switch (rc) { case SUCCESS: client->hostname = hostname; - redsocks_log_error(client, LOG_INFO, "found hostname %s, now connecting", client->hostname); - + redsocks_log_error(client, LOG_INFO, "found hostname %s,", client->hostname); + case DATA_MISSING: + redsocks_log_error(client, LOG_INFO, "now connecting..."); if (client->instance->relay_ss->connect_relay) { client->instance->relay_ss->connect_relay(client); } else { @@ -981,11 +985,6 @@ static void redsocks_hostname_reader(struct bufferevent *buffev, void *_arg) break; - case DATA_MISSING: - redsocks_log_error(client, LOG_INFO, "data is missing"); - - break; - case FATAL_ERROR: /* passthourgh */ default: redsocks_drop_client(client); From 9c3f661068a64bc135c7fa57e15a3406f3900fd1 Mon Sep 17 00:00:00 2001 From: andronoob Date: Tue, 20 Apr 2021 22:36:18 +0800 Subject: [PATCH 03/11] remove logger.c from sniproxy --- Makefile | 2 +- logger.c | 501 ------------------------------------------------------- logger.h | 66 -------- tls.c | 16 +- 4 files changed, 9 insertions(+), 576 deletions(-) delete mode 100644 logger.c delete mode 100644 logger.h diff --git a/Makefile b/Makefile index 00e9d33a..47b9c6aa 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -include make.conf -OBJS := parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o dnsu2t.o tls.o logger.o gen/version.o +OBJS := parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o dnsu2t.o tls.o gen/version.o ifeq ($(DBG_BUILD),1) OBJS += debug.o endif diff --git a/logger.c b/logger.c deleted file mode 100644 index 26aad8ca..00000000 --- a/logger.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * Copyright (c) 2013, Dustin Lundquist - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "logger.h" - -struct Logger { - struct LogSink *sink; - int priority; - int facility; - int reference_count; -}; - -struct LogSink { - enum { - LOG_SINK_SYSLOG, - LOG_SINK_STDERR, - LOG_SINK_FILE - } type; - const char *filepath; - - FILE *fd; - int reference_count; - SLIST_ENTRY(LogSink) entries; -}; - - -static struct Logger *default_logger = NULL; -static SLIST_HEAD(LogSink_head, LogSink) sinks = SLIST_HEAD_INITIALIZER(sinks); - - -static void free_logger(struct Logger *); -static void init_default_logger(); -static void vlog_msg(struct Logger *, int, const char *, va_list); -static void free_at_exit(); -static int lookup_syslog_facility(const char *); -static const char *timestamp(char *, size_t); -static struct LogSink *obtain_stderr_sink(); -static struct LogSink *obtain_syslog_sink(); -static struct LogSink *obtain_file_sink(const char *); -static struct LogSink *log_sink_ref_get(struct LogSink *); -static void log_sink_ref_put(struct LogSink *); -static void free_sink(struct LogSink *); - - -struct Logger * -new_syslog_logger(const char *facility) { - struct Logger *logger = malloc(sizeof(struct Logger)); - if (logger != NULL) { - logger->sink = obtain_syslog_sink(); - if (logger->sink == NULL) { - free(logger); - return NULL; - } - logger->priority = LOG_DEBUG; - logger->facility = lookup_syslog_facility(facility); - logger->reference_count = 0; - - log_sink_ref_get(logger->sink); - } - - return logger; -} - -struct Logger * -new_file_logger(const char *filepath) { - struct Logger *logger = malloc(sizeof(struct Logger)); - if (logger != NULL) { - logger->sink = obtain_file_sink(filepath); - if (logger->sink == NULL) { - free(logger); - return NULL; - } - logger->priority = LOG_DEBUG; - logger->facility = 0; - logger->reference_count = 0; - - log_sink_ref_get(logger->sink); - } - - return logger; -} - -void -reopen_loggers() { - struct LogSink *sink; - - SLIST_FOREACH(sink, &sinks, entries) { - if (sink->type == LOG_SINK_SYSLOG) { - closelog(); - openlog("redsocks", LOG_PID, 0); - } else if (sink->type == LOG_SINK_FILE) { - sink->fd = freopen(sink->filepath, "a", sink->fd); - if (sink->fd == NULL) - err("failed to reopen log file %s: %s", - sink->filepath, strerror(errno)); - else - setvbuf(sink->fd, NULL, _IOLBF, 0); - } - } -} - -void -set_default_logger(struct Logger *new_logger) { - struct Logger *old_default_logger = default_logger; - - assert(new_logger != NULL); - default_logger = logger_ref_get(new_logger); - logger_ref_put(old_default_logger); -} - -void -set_logger_priority(struct Logger *logger, int priority) { - assert(logger != NULL); - assert(priority >= LOG_EMERG && priority <= LOG_DEBUG); - logger->priority = priority; -} - -void -logger_ref_put(struct Logger *logger) { - if (logger == NULL) - return; - - assert(logger->reference_count > 0); - logger->reference_count--; - if (logger->reference_count == 0) - free_logger(logger); -} - -struct Logger * -logger_ref_get(struct Logger *logger) { - if (logger != NULL) - logger->reference_count++; - - return logger; -} - -static void -free_logger(struct Logger *logger) { - if (logger == NULL) - return; - - log_sink_ref_put(logger->sink); - logger->sink = NULL; - - free(logger); -} - -void -log_msg(struct Logger *logger, int priority, const char *format, ...) { - va_list args; - - va_start(args, format); - vlog_msg(logger, priority, format, args); - va_end(args); -} - -void -fatal(const char *format, ...) { - va_list args; - - init_default_logger(); - - va_start(args, format); - vlog_msg(default_logger, LOG_CRIT, format, args); - va_end(args); - - exit(EXIT_FAILURE); -} - -void -err(const char *format, ...) { - va_list args; - - init_default_logger(); - - va_start(args, format); - vlog_msg(default_logger, LOG_ERR, format, args); - va_end(args); -} - -void -warn(const char *format, ...) { - va_list args; - - init_default_logger(); - - va_start(args, format); - vlog_msg(default_logger, LOG_WARNING, format, args); - va_end(args); -} - -void -notice(const char *format, ...) { - va_list args; - - init_default_logger(); - - va_start(args, format); - vlog_msg(default_logger, LOG_NOTICE, format, args); - va_end(args); -} - -void -info(const char *format, ...) { - va_list args; - - init_default_logger(); - - va_start(args, format); - vlog_msg(default_logger, LOG_INFO, format, args); - va_end(args); -} - -void -debug(const char *format, ...) { - va_list args; - - init_default_logger(); - - va_start(args, format); - vlog_msg(default_logger, LOG_DEBUG, format, args); - va_end(args); -} - -static void -vlog_msg(struct Logger *logger, int priority, const char *format, va_list args) { - assert(logger != NULL); - - if (priority > logger->priority) - return; - - if (logger->sink->type == LOG_SINK_SYSLOG) { - vsyslog(logger->facility|logger->priority, format, args); - } else if (logger->sink->fd != NULL) { - char buffer[1024]; - - timestamp(buffer, sizeof(buffer)); - size_t len = strlen(buffer); - - vsnprintf(buffer + len, sizeof(buffer) - len, format, args); - buffer[sizeof(buffer) - 1] = '\0'; /* ensure buffer null terminated */ - - fprintf(logger->sink->fd, "%s\n", buffer); - } -} - -static void -init_default_logger() { - struct Logger *logger = NULL; - - if (default_logger != NULL) - return; - - logger = malloc(sizeof(struct Logger)); - if (logger != NULL) { - logger->sink = obtain_stderr_sink(); - if (logger->sink == NULL) { - free(logger); - return; - } - logger->priority = LOG_DEBUG; - logger->facility = 0; - logger->reference_count = 0; - - log_sink_ref_get(logger->sink); - } - - if (logger == NULL) - return; - - atexit(free_at_exit); - - default_logger = logger_ref_get(logger); -} - -static void -free_at_exit() { - logger_ref_put(default_logger); - default_logger = NULL; -} - -static int -lookup_syslog_facility(const char *facility) { - static const struct { - const char *name; - int number; - } facilities[] = { - { "auth", LOG_AUTH }, - { "cron", LOG_CRON }, - { "daemon", LOG_DAEMON }, - { "ftp", LOG_FTP }, - { "local0", LOG_LOCAL0 }, - { "local1", LOG_LOCAL1 }, - { "local2", LOG_LOCAL2 }, - { "local3", LOG_LOCAL3 }, - { "local4", LOG_LOCAL4 }, - { "local5", LOG_LOCAL5 }, - { "local6", LOG_LOCAL6 }, - { "local7", LOG_LOCAL7 }, - { "mail", LOG_MAIL }, - { "news", LOG_NEWS }, - { "user", LOG_USER }, - { "uucp", LOG_UUCP }, - }; - - for (size_t i = 0; i < sizeof(facilities) / sizeof(facilities[0]); i++) - if (strncasecmp(facilities[i].name, facility, strlen(facility)) == 0) - return facilities[i].number; - - /* fall back value */ - return LOG_USER; -} - -static struct LogSink * -obtain_stderr_sink() { - struct LogSink *sink; - - SLIST_FOREACH(sink, &sinks, entries) { - if (sink->type == LOG_SINK_STDERR) - return sink; - } - - sink = malloc(sizeof(struct LogSink)); - if (sink != NULL) { - sink->type = LOG_SINK_STDERR; - sink->filepath = NULL; - sink->fd = stderr; - sink->reference_count = 0; - - SLIST_INSERT_HEAD(&sinks, sink, entries); - } - - return sink; -} - -static struct LogSink * -obtain_syslog_sink() { - struct LogSink *sink; - - SLIST_FOREACH(sink, &sinks, entries) { - if (sink->type == LOG_SINK_SYSLOG) - return sink; - } - - sink = malloc(sizeof(struct LogSink)); - if (sink != NULL) { - sink->type = LOG_SINK_SYSLOG; - sink->filepath = NULL; - sink->fd = NULL; - sink->reference_count = 0; - - openlog("redsocks", LOG_PID, 0); - - SLIST_INSERT_HEAD(&sinks, sink, entries); - } - - return sink; -} - -static struct LogSink * -obtain_file_sink(const char *filepath) { - struct LogSink *sink; - - if (filepath == NULL) - return NULL; - - SLIST_FOREACH(sink, &sinks, entries) { - if (sink->type == LOG_SINK_FILE && - strcmp(sink->filepath, filepath) == 0) - return sink; - } - - sink = malloc(sizeof(struct LogSink)); - if (sink == NULL) - return NULL; - - - FILE *fd = fopen(filepath, "a"); - if (fd == NULL) { - free(sink); - err("Failed to open new log file: %s", filepath); - return NULL; - } - setvbuf(fd, NULL, _IOLBF, 0); - - sink->type = LOG_SINK_FILE; - sink->filepath = strdup(filepath); - sink->fd = fd; - sink->reference_count = 0; - - SLIST_INSERT_HEAD(&sinks, sink, entries); - - return sink; -} - -static struct LogSink * -log_sink_ref_get(struct LogSink *sink) { - if (sink != NULL) - sink->reference_count++; - - return sink; -} - -static void -log_sink_ref_put(struct LogSink *sink) { - if (sink == NULL) - return; - - assert(sink->reference_count > 0); - sink->reference_count--; - if (sink->reference_count == 0) - free_sink(sink); -} - -static void -free_sink(struct LogSink *sink) { - if (sink == NULL) - return; - - SLIST_REMOVE(&sinks, sink, LogSink, entries); - - switch(sink->type) { - case LOG_SINK_SYSLOG: - closelog(); - break; - case LOG_SINK_STDERR: - fflush(sink->fd); - sink->fd = NULL; - break; - case LOG_SINK_FILE: - fclose(sink->fd); - sink->fd = NULL; - free((char *)sink->filepath); - sink->filepath = NULL; - break; - default: - assert(0); - } - - free(sink); -} - -static const char * -timestamp(char *dst, size_t dst_len) { - /* TODO change to ev_now() */ - time_t now = time(NULL); - static struct { - time_t when; - char string[32]; - } timestamp_cache = { .when = 0, .string = {'\0'} }; - - if (now != timestamp_cache.when) { -#ifdef RFC3339_TIMESTAMP - struct tm *tmp = gmtime(&now); - strftime(timestamp_cache.string, sizeof(timestamp_cache.string), - "%FT%TZ ", tmp); -#else - struct tm *tmp = localtime(&now); - strftime(timestamp_cache.string, sizeof(timestamp_cache.string), - "%F %T ", tmp); -#endif - - timestamp_cache.when = now; - } - - if (dst != NULL) - strncpy(dst, timestamp_cache.string, dst_len); - - return timestamp_cache.string; -} diff --git a/logger.h b/logger.h deleted file mode 100644 index 657e8216..00000000 --- a/logger.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2013, Dustin Lundquist - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef LOGGER_H -#define LOGGER_H - -struct Logger; - -#define LOG_EMERG 0 -#define LOG_ALERT 1 -#define LOG_CRIT 2 -#define LOG_ERR 3 -#define LOG_WARNING 4 -#define LOG_NOTICE 5 -#define LOG_INFO 6 -#define LOG_DEBUG 7 - -struct Logger *new_syslog_logger(const char *facility); -struct Logger *new_file_logger(const char *filepath); -void set_default_logger(struct Logger *); -void set_logger_priority(struct Logger *, int); -struct Logger *logger_ref_get(struct Logger *); -void logger_ref_put(struct Logger *); -void reopen_loggers(); - -/* Shorthand to log to global error log */ -void fatal(const char *, ...) - __attribute__ ((format (printf, 1, 2))) - __attribute__ ((noreturn)); -void err(const char *, ...) - __attribute__ ((format (printf, 1, 2))); -void warn(const char *, ...) - __attribute__ ((format (printf, 1, 2))); -void notice(const char *, ...) - __attribute__ ((format (printf, 1, 2))); -void info(const char *, ...) - __attribute__ ((format (printf, 1, 2))); -void debug(const char *, ...) - __attribute__ ((format (printf, 1, 2))); - -void log_msg(struct Logger *, int, const char *, ...) - __attribute__ ((format (printf, 3, 4))); - -#endif diff --git a/tls.c b/tls.c index 744a3edd..b2deb50c 100644 --- a/tls.c +++ b/tls.c @@ -36,7 +36,7 @@ #include #include "tls.h" #include "protocol.h" -#include "logger.h" +#include "log.h" #define SERVER_NAME_LEN 256 #define TLS_HEADER_LEN 5 @@ -100,20 +100,20 @@ parse_tls_header(const uint8_t *data, size_t data_len, char **hostname) { * See RFC5246 Appendix E.2 */ if (data[0] & 0x80 && data[2] == 1) { - debug("Received SSL 2.0 Client Hello which can not support SNI."); + log_error(LOG_DEBUG, "Received SSL 2.0 Client Hello which can not support SNI."); return -2; } tls_content_type = data[0]; if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) { - debug("Request did not begin with TLS handshake."); + log_error(LOG_DEBUG, "Request did not begin with TLS handshake."); return -5; } tls_version_major = data[1]; tls_version_minor = data[2]; if (tls_version_major < 3) { - debug("Received SSL %" PRIu8 ".%" PRIu8 " handshake which can not support SNI.", + log_error(LOG_DEBUG, "Received SSL %" PRIu8 ".%" PRIu8 " handshake which can not support SNI.", tls_version_major, tls_version_minor); return -2; @@ -135,7 +135,7 @@ parse_tls_header(const uint8_t *data, size_t data_len, char **hostname) { return -5; } if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { - debug("Not a client hello"); + log_error(LOG_DEBUG, "Not a client hello"); return -5; } @@ -168,7 +168,7 @@ parse_tls_header(const uint8_t *data, size_t data_len, char **hostname) { pos += 1 + len; if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) { - debug("Received SSL 3.0 handshake without extensions"); + log_error(LOG_DEBUG, "Received SSL 3.0 handshake without extensions"); return -2; } @@ -228,7 +228,7 @@ parse_server_name_extension(const uint8_t *data, size_t data_len, case 0x00: /* host_name */ *hostname = malloc(len + 1); if (*hostname == NULL) { - err("malloc() failure"); + log_error(LOG_ERR, "malloc() failure"); return -4; } @@ -238,7 +238,7 @@ parse_server_name_extension(const uint8_t *data, size_t data_len, return len; default: - debug("Unknown server name extension name type: %" PRIu8, + log_error(LOG_DEBUG, "Unknown server name extension name type: %" PRIu8, data[pos]); } pos += 3 + len; From 0389c2f3b102e08bcff4e23de4df047f8d5dc028 Mon Sep 17 00:00:00 2001 From: andronoob Date: Wed, 21 Apr 2021 00:15:07 +0800 Subject: [PATCH 04/11] add back http host parsing --- .gitignore | 1 + Makefile | 24 +++++++++-- redsocks.c | 98 ++++++++++++++++++++++++++++++++++++++++--- redsocks.conf.example | 4 ++ redsocks.h | 1 + 5 files changed, 119 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index d2a3c236..9821329d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ config.h tags redsocks .depend +http-parser* /gen diff --git a/Makefile b/Makefile index 47b9c6aa..8f4b32f9 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,9 @@ -include make.conf + +LIBHTTP_VERSION := 2.9.4 +LIBHTTP_NAME := http-parser-$(LIBHTTP_VERSION) +LIBHTTP_CFLAGS := -I./http-parser-$(LIBHTTP_VERSION) -L./http-parser-$(LIBHTTP_VERSION) + OBJS := parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o dnsu2t.o tls.o gen/version.o ifeq ($(DBG_BUILD),1) OBJS += debug.o @@ -10,21 +15,33 @@ OUT := redsocks VERSION := 0.5 LIBS := -levent_core +LIBS += -lhttp_parser ifeq ($(DBG_BUILD),1) # -levent_extra is required only for `http` and `debug` LIBS += -levent_extra endif +CFLAGS += $(LIBHTTP_CFLAGS) CFLAGS += -g -O2 # _GNU_SOURCE is used to get splice(2), it also implies _BSD_SOURCE override CFLAGS += -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall all: $(OUT) -.PHONY: all clean distclean test +.PHONY: all clean distclean test http-parser tags: *.c *.h ctags -R +$(LIBHTTP_NAME): + wget https://github.com/nodejs/http-parser/archive/v$(LIBHTTP_VERSION).tar.gz + tar -zxf v$(LIBHTTP_VERSION).tar.gz + rm -f v$(LIBHTTP_VERSION).tar.gz + +$(LIBHTTP_NAME)/libhttp_parser.o: + cd $(LIBHTTP_NAME) && make package + +http-parser: $(LIBHTTP_NAME) $(LIBHTTP_NAME)/libhttp_parser.o + $(CONF): @case `uname` in \ Linux*) \ @@ -90,8 +107,8 @@ $(DEPS): $(SRCS) -include $(DEPS) -$(OUT): $(OBJS) - $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) +$(OUT): http-parser $(OBJS) + $(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) clean: $(RM) $(OUT) $(CONF) $(OBJS) @@ -99,6 +116,7 @@ clean: distclean: clean $(RM) tags $(DEPS) $(RM) -r gen + $(RM) -rf $(LIBHTTP_NAME) tests/__build-tstamp__: $(OUT) tests/[a-z]* tests/[a-z]*/* cd tests && ./build diff --git a/redsocks.c b/redsocks.c index c1e2d21b..65c1762d 100644 --- a/redsocks.c +++ b/redsocks.c @@ -38,10 +38,17 @@ #include "utils.h" #include "libevent-compat.h" #include "tls.h" +#include "http_parser.h" #define MINIMUM_HOST_READ (10) #define MAXIMUM_HOST_READ (0) +struct http_parser_data { + const char *last_header_field; + const char *http_host; + size_t http_host_length; +}; + typedef enum _redsocks_hostname_read_rc { SUCCESS = 0, DATA_MISSING = 1, @@ -91,6 +98,7 @@ static parser_entry redsocks_entries[] = { .key = "password", .type = pt_pchar }, { .key = "listenq", .type = pt_uint16 }, { .key = "parse_sni_host", .type = pt_bool }, + { .key = "parse_http_host", .type = pt_bool }, { .key = "splice", .type = pt_bool }, { .key = "disclose_src", .type = pt_disclose_src }, { .key = "on_proxy_fail", .type = pt_on_proxy_fail }, @@ -159,6 +167,7 @@ static int redsocks_onenter(parser_section *section) (strcmp(entry->key, "password") == 0) ? (void*)&instance->config.password : (strcmp(entry->key, "listenq") == 0) ? (void*)&instance->config.listenq : (strcmp(entry->key, "parse_sni_host") == 0) ? (void*)&instance->config.parse_sni_host : + (strcmp(entry->key, "parse_http_host") == 0) ? (void*)&instance->config.parse_http_host : (strcmp(entry->key, "splice") == 0) ? (void*)&instance->config.use_splice : (strcmp(entry->key, "disclose_src") == 0) ? (void*)&instance->config.disclose_src : (strcmp(entry->key, "on_proxy_fail") == 0) ? (void*)&instance->config.on_proxy_fail : @@ -880,6 +889,31 @@ int redsocks_read_expected(redsocks_client *client, struct evbuffer *input, void } } +static int redsocks_http_parser_on_header_field(http_parser *parser, const char *at, size_t length) +{ + struct http_parser_data *parser_data = (struct http_parser_data *) parser->data; + + parser_data->last_header_field = at; + + return 0; +} + +static int redsocks_http_parser_on_header_value(http_parser *parser, const char *at, size_t length) +{ + struct http_parser_data *parser_data = (struct http_parser_data *) parser->data; + + if (0 != strncasecmp(parser_data->last_header_field, "host", sizeof("host") - 1)) { + return 0; + } + + parser_data->http_host = at; + parser_data->http_host_length = length; + + http_parser_pause(parser, 1); + + return 0; +} + static int redsocks_peek_buffer(redsocks_client *client, struct bufferevent *buffev, char **peek_buffer, size_t *peek_size) { int n = 0, i = 0; @@ -943,6 +977,55 @@ static redsocks_hostname_read_rc redsocks_read_sni(redsocks_client *client, char return FATAL_ERROR; } +static redsocks_hostname_read_rc redsocks_read_http_host(redsocks_client *client, char *read_buffer, size_t read_buffer_size, char **hostname) +{ + http_parser parser; + http_parser_settings parser_settings; + struct http_parser_data parser_data; + char *temp_hostname = NULL; + int rc = HPE_UNKNOWN; + + memset(&parser, 0, sizeof(parser)); + memset(&parser_settings, 0, sizeof(parser_settings)); + memset(&parser_data, 0, sizeof(parser_data)); + + http_parser_init(&parser, HTTP_REQUEST); + + parser.data = &parser_data; + parser_settings.on_header_field = redsocks_http_parser_on_header_field; + parser_settings.on_header_value = redsocks_http_parser_on_header_value; + + rc = http_parser_execute(&parser, &parser_settings, read_buffer, read_buffer_size); + + if (rc != read_buffer_size && + HTTP_PARSER_ERRNO(&parser) != HPE_PAUSED) { + redsocks_log_error(client, LOG_ERR, "error at http parser library: %s", + http_errno_description(HTTP_PARSER_ERRNO(&parser))); + return FATAL_ERROR; + } + + if (rc == read_buffer_size && NULL == parser_data.http_host) { + return DATA_MISSING; + } + + if (parser_data.http_host && parser_data.http_host_length) { + temp_hostname = (char *) malloc(parser_data.http_host_length + 1); + if (NULL == temp_hostname) { + return FATAL_ERROR; + } + + memset(temp_hostname, 0, parser_data.http_host_length + 1); + memcpy(temp_hostname, parser_data.http_host, parser_data.http_host_length); + temp_hostname[parser_data.http_host_length + 1] = '\0'; + *hostname = temp_hostname; + + return SUCCESS; + } + + /* should be unreachable */ + return FATAL_ERROR; +} + static void redsocks_hostname_reader(struct bufferevent *buffev, void *_arg) { redsocks_client *client = _arg; @@ -951,9 +1034,9 @@ static void redsocks_hostname_reader(struct bufferevent *buffev, void *_arg) char *hostname = NULL; redsocks_hostname_read_rc rc = FATAL_ERROR; - assert(client->instance->config.parse_sni_host); + assert(client->instance->config.parse_sni_host || client->instance->config.parse_http_host); - if (!client->instance->config.parse_sni_host) { + if (!client->instance->config.parse_sni_host && !client->instance->config.parse_http_host) { return; } @@ -962,11 +1045,14 @@ static void redsocks_hostname_reader(struct bufferevent *buffev, void *_arg) return; } - redsocks_log_error(client, LOG_INFO, "searching for hostname by sni"); - if (client->instance->config.parse_sni_host) { + redsocks_log_error(client, LOG_INFO, "searching for hostname by TLS SNI"); rc = redsocks_read_sni(client, read_buffer, read_buffer_size, &hostname); } + if (rc != SUCCESS && rc != FATAL_ERROR && client->instance->config.parse_http_host) { + redsocks_log_error(client, LOG_INFO, "searching for hostname by HTTP Host header"); + rc = redsocks_read_http_host(client, read_buffer, read_buffer_size, &hostname); + } client->hostname = NULL; @@ -1351,7 +1437,7 @@ static void redsocks_accept_client(int fd, short what, void *_arg) redsocks_touch_client(client); - if (!self->config.parse_sni_host) { + if (!self->config.parse_sni_host && !self->config.parse_http_host) { client->client = bufferevent_new(client_fd, NULL, NULL, redsocks_event_error, client); } else { client->client = bufferevent_new(client_fd, redsocks_hostname_reader, NULL, redsocks_event_error, client); @@ -1372,7 +1458,7 @@ static void redsocks_accept_client(int fd, short what, void *_arg) redsocks_log_error(client, LOG_INFO, "accepted"); - if (self->config.parse_sni_host) { + if (self->config.parse_sni_host || self->config.parse_http_host) { client->client->wm_read.low = MINIMUM_HOST_READ; client->client->wm_read.high = MAXIMUM_HOST_READ; /* We wait first for the client to give us the host */ diff --git a/redsocks.conf.example b/redsocks.conf.example index 8224b256..d46caf91 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -116,6 +116,10 @@ redsocks { // parse hostname from TLS SNI extension, // so that HTTP CONNECT can use hostname rather than IP address // parse_sni_host = true; + + // parse hostname from plaintext HTTP Host header, + // so that HTTP CONNECT can use hostname rather than IP address + // parse_http_host = true; } redudp { diff --git a/redsocks.h b/redsocks.h index 9efe6b51..2e84ba3b 100644 --- a/redsocks.h +++ b/redsocks.h @@ -33,6 +33,7 @@ typedef struct redsocks_config_t { char *login; char *password; bool parse_sni_host; + bool parse_http_host; uint16_t listenq; bool use_splice; enum disclose_src_e disclose_src; From 809aa9c841494c35722158722803e5831b03bb12 Mon Sep 17 00:00:00 2001 From: andronoob Date: Wed, 21 Apr 2021 02:41:43 +0800 Subject: [PATCH 05/11] fix URLs with a colon (host:port); trivial tab/whitespace indent fix; note remote DNS in example conf --- redsocks.c | 21 ++++++++++++++------- redsocks.conf.example | 6 ++++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/redsocks.c b/redsocks.c index 65c1762d..19ef6cc6 100644 --- a/redsocks.c +++ b/redsocks.c @@ -1017,6 +1017,13 @@ static redsocks_hostname_read_rc redsocks_read_http_host(redsocks_client *client memset(temp_hostname, 0, parser_data.http_host_length + 1); memcpy(temp_hostname, parser_data.http_host, parser_data.http_host_length); temp_hostname[parser_data.http_host_length + 1] = '\0'; + + /* handle the http://host:port/ situation. + this is not yet handled by http-parser currently: + https://github.com/nodejs/http-parser/issues/501 */ + char *colon_ptr = strchr(temp_hostname, ':'); + if (colon_ptr != NULL) memset(colon_ptr, 0, parser_data.http_host_length + 1 - (colon_ptr - temp_hostname)); + *hostname = temp_hostname; return SUCCESS; @@ -1060,7 +1067,7 @@ static void redsocks_hostname_reader(struct bufferevent *buffev, void *_arg) case SUCCESS: client->hostname = hostname; - redsocks_log_error(client, LOG_INFO, "found hostname %s,", client->hostname); + redsocks_log_error(client, LOG_INFO, "found hostname %s,", client->hostname); case DATA_MISSING: redsocks_log_error(client, LOG_INFO, "now connecting..."); if (client->instance->relay_ss->connect_relay) { @@ -1458,12 +1465,12 @@ static void redsocks_accept_client(int fd, short what, void *_arg) redsocks_log_error(client, LOG_INFO, "accepted"); - if (self->config.parse_sni_host || self->config.parse_http_host) { - client->client->wm_read.low = MINIMUM_HOST_READ; - client->client->wm_read.high = MAXIMUM_HOST_READ; - /* We wait first for the client to give us the host */ - return; - } + if (self->config.parse_sni_host || self->config.parse_http_host) { + client->client->wm_read.low = MINIMUM_HOST_READ; + client->client->wm_read.high = MAXIMUM_HOST_READ; + /* We wait first for the client to give us the host */ + return; + } if (self->relay_ss->connect_relay) self->relay_ss->connect_relay(client); diff --git a/redsocks.conf.example b/redsocks.conf.example index d46caf91..a5a8a3f9 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -114,11 +114,13 @@ redsocks { // on_proxy_fail = close; // parse hostname from TLS SNI extension, - // so that HTTP CONNECT can use hostname rather than IP address + // so that HTTP CONNECT can use hostname rather than IP address. + // in this case, the hostname is supposed to be resoved remotely. // parse_sni_host = true; // parse hostname from plaintext HTTP Host header, - // so that HTTP CONNECT can use hostname rather than IP address + // so that HTTP CONNECT can use hostname rather than IP address. + // in this case, the hostname is supposed to be resoved remotely. // parse_http_host = true; } From c9028602f8d5935f1f71dda68a761d8dfbd3b7bc Mon Sep 17 00:00:00 2001 From: andronoob Date: Wed, 21 Apr 2021 03:57:59 +0800 Subject: [PATCH 06/11] fix assert failure while POSTing data --- redsocks.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/redsocks.c b/redsocks.c index 19ef6cc6..f6c05402 100644 --- a/redsocks.c +++ b/redsocks.c @@ -1047,6 +1047,10 @@ static void redsocks_hostname_reader(struct bufferevent *buffev, void *_arg) return; } + if (client->relay != NULL) { + return; + } + if (0 != redsocks_peek_buffer(client, buffev, &read_buffer, &read_buffer_size)) { redsocks_drop_client(client); return; From 0121837a34bbde622dbee45205fb18fc8b606612 Mon Sep 17 00:00:00 2001 From: andronoob Date: Wed, 21 Apr 2021 04:24:52 +0800 Subject: [PATCH 07/11] default value of parse_sni/http_host should be false --- redsocks.conf.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redsocks.conf.example b/redsocks.conf.example index a5a8a3f9..4a500f3a 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -116,12 +116,12 @@ redsocks { // parse hostname from TLS SNI extension, // so that HTTP CONNECT can use hostname rather than IP address. // in this case, the hostname is supposed to be resoved remotely. - // parse_sni_host = true; + // parse_sni_host = false; // parse hostname from plaintext HTTP Host header, // so that HTTP CONNECT can use hostname rather than IP address. // in this case, the hostname is supposed to be resoved remotely. - // parse_http_host = true; + // parse_http_host = false; } redudp { From 57551744f62298e9811cb1e26803e07b474bf29e Mon Sep 17 00:00:00 2001 From: andronoob Date: Wed, 21 Apr 2021 14:52:29 +0800 Subject: [PATCH 08/11] do not drop non http/tls traffic --- redsocks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/redsocks.c b/redsocks.c index f6c05402..71bbdc73 100644 --- a/redsocks.c +++ b/redsocks.c @@ -1001,7 +1001,8 @@ static redsocks_hostname_read_rc redsocks_read_http_host(redsocks_client *client HTTP_PARSER_ERRNO(&parser) != HPE_PAUSED) { redsocks_log_error(client, LOG_ERR, "error at http parser library: %s", http_errno_description(HTTP_PARSER_ERRNO(&parser))); - return FATAL_ERROR; + //return FATAL_ERROR; + return DATA_MISSING; /* Something like "invalid HTTP method" should not be fatal */ } if (rc == read_buffer_size && NULL == parser_data.http_host) { From 9b0a5da12c3f42ca3f4e45c9d8b57cd42a5c88d4 Mon Sep 17 00:00:00 2001 From: andronoob Date: Fri, 23 Apr 2021 01:03:23 +0800 Subject: [PATCH 09/11] fix array index out-of-bounds --- redsocks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redsocks.c b/redsocks.c index 71bbdc73..7f9d31ec 100644 --- a/redsocks.c +++ b/redsocks.c @@ -1017,7 +1017,7 @@ static redsocks_hostname_read_rc redsocks_read_http_host(redsocks_client *client memset(temp_hostname, 0, parser_data.http_host_length + 1); memcpy(temp_hostname, parser_data.http_host, parser_data.http_host_length); - temp_hostname[parser_data.http_host_length + 1] = '\0'; + temp_hostname[parser_data.http_host_length] = '\0'; //this should be redundant. (should already be covered by memset) /* handle the http://host:port/ situation. this is not yet handled by http-parser currently: From 70bc8456d6d23ef070a4a1def4921394470ec217 Mon Sep 17 00:00:00 2001 From: andronoob Date: Fri, 23 Apr 2021 01:50:18 +0800 Subject: [PATCH 10/11] use git submodule instead of wget --- .gitignore | 1 - .gitmodules | 3 +++ Makefile | 18 +++++++----------- http-parser | 1 + 4 files changed, 11 insertions(+), 12 deletions(-) create mode 100644 .gitmodules create mode 160000 http-parser diff --git a/.gitignore b/.gitignore index 9821329d..d2a3c236 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,4 @@ config.h tags redsocks .depend -http-parser* /gen diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..1aa7912c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "http-parser"] + path = http-parser + url = https://github.com/nodejs/http-parser/ diff --git a/Makefile b/Makefile index 8f4b32f9..f77307f8 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,6 @@ -include make.conf -LIBHTTP_VERSION := 2.9.4 -LIBHTTP_NAME := http-parser-$(LIBHTTP_VERSION) -LIBHTTP_CFLAGS := -I./http-parser-$(LIBHTTP_VERSION) -L./http-parser-$(LIBHTTP_VERSION) +LIBHTTP_CFLAGS := -I./http-parser -L./http-parser OBJS := parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o dnsu2t.o tls.o gen/version.o ifeq ($(DBG_BUILD),1) @@ -32,15 +30,13 @@ all: $(OUT) tags: *.c *.h ctags -R -$(LIBHTTP_NAME): - wget https://github.com/nodejs/http-parser/archive/v$(LIBHTTP_VERSION).tar.gz - tar -zxf v$(LIBHTTP_VERSION).tar.gz - rm -f v$(LIBHTTP_VERSION).tar.gz +http-parser-download: + git submodule update --init -$(LIBHTTP_NAME)/libhttp_parser.o: - cd $(LIBHTTP_NAME) && make package +http-parser-build: + cd http-parser && make package -http-parser: $(LIBHTTP_NAME) $(LIBHTTP_NAME)/libhttp_parser.o +http-parser: http-parser-download http-parser-build $(CONF): @case `uname` in \ @@ -116,7 +112,7 @@ clean: distclean: clean $(RM) tags $(DEPS) $(RM) -r gen - $(RM) -rf $(LIBHTTP_NAME) + cd http-parser && make clean tests/__build-tstamp__: $(OUT) tests/[a-z]* tests/[a-z]*/* cd tests && ./build diff --git a/http-parser b/http-parser new file mode 160000 index 00000000..2343fd6b --- /dev/null +++ b/http-parser @@ -0,0 +1 @@ +Subproject commit 2343fd6b5214b2ded2cdcf76de2bf60903bb90cd From 554d78401e2a77063529b47f83ce1458657a81fb Mon Sep 17 00:00:00 2001 From: andronoob Date: Sun, 25 Apr 2021 13:45:20 +0800 Subject: [PATCH 11/11] fix typo; note about remote-DNS --- redsocks.conf.example | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/redsocks.conf.example b/redsocks.conf.example index 4a500f3a..4258cb6d 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -115,12 +115,14 @@ redsocks { // parse hostname from TLS SNI extension, // so that HTTP CONNECT can use hostname rather than IP address. - // in this case, the hostname is supposed to be resoved remotely. + // in this case, the hostname is supposed to be resolved remotely. + // (in other words - remote-DNS) // parse_sni_host = false; // parse hostname from plaintext HTTP Host header, // so that HTTP CONNECT can use hostname rather than IP address. - // in this case, the hostname is supposed to be resoved remotely. + // in this case, the hostname is supposed to be resolved remotely. + // (in other words - remote-DNS) // parse_http_host = false; }