From e7b7f3623f92aaec8eac13f86eb5e462fc3625de Mon Sep 17 00:00:00 2001 From: Liu Dongmiao Date: Fri, 22 Mar 2024 11:37:02 +0800 Subject: [PATCH] both: shadowsocks over tls --- common.c | 38 ++++++++++++++++++++++++++++++++++++++ common.h | 5 +++++ run-test.sh | 12 ++++++++++++ wss-proxy-client.c | 38 ++++++++++++++++++++++++++++++++++---- wss-proxy-server.c | 11 ++++++++++- 5 files changed, 99 insertions(+), 5 deletions(-) diff --git a/common.c b/common.c index f5babc1..c047332 100644 --- a/common.c +++ b/common.c @@ -591,6 +591,44 @@ void tunnel_wss(struct bufferevent *raw, struct evhttp_connection *wss) { bufferevent_setcb(raw, raw_forward_cb, NULL, raw_event_cb_wss, wss); } +static void wss_event_cb_ss(struct bufferevent *tev, short event, void *raw) { + uint16_t port; + struct evhttp_connection *wss; + (void) tev; + if (event & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { + bufferevent_getcb(raw, NULL, NULL, NULL, (void **) &wss); +#ifdef WSS_PROXY_CLIENT + port = get_peer_port(raw); +#endif +#ifdef WSS_PROXY_SERVER + port = get_http_port(wss); +#endif + LOGD("connection %u closing from wss %p (won't send close), event: 0x%02x", port, wss, event); + bufferevent_free(raw); + evhttp_connection_free(wss); + } +} + +static void raw_forward_cb_ss(struct bufferevent *raw, void *wss) { + struct evbuffer *src; + struct evbuffer *dst; + + src = bufferevent_get_input(raw); + dst = bufferevent_get_output(evhttp_connection_get_bufferevent(wss)); + evbuffer_add_buffer(dst, src); +} + +void tunnel_ss(struct bufferevent *raw, struct evhttp_connection *wss) { + struct bufferevent *tev; + + tev = evhttp_connection_get_bufferevent(wss); + bufferevent_enable(tev, EV_READ | EV_WRITE); + bufferevent_setcb(tev, wss_forward_cb, NULL, wss_event_cb_ss, raw); + + bufferevent_enable(raw, EV_READ | EV_WRITE); + bufferevent_setcb(raw, raw_forward_cb_ss, NULL, raw_event_cb, wss); +} + #ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK void ssl_keylog_callback(const SSL *ssl, const char *line) { char *keylog_file_name; diff --git a/common.h b/common.h index 51a5dd5..f8d78ca 100644 --- a/common.h +++ b/common.h @@ -94,6 +94,11 @@ void tunnel_wss(struct bufferevent *raw, struct evhttp_connection *wss); */ int send_close(struct bufferevent *raw, uint16_t reason); +#define X_UPGRADE "X-Upgrade" +#define SHADOWSOCKS "shadowsocks" +#define IS_SHADOWSOCKS(x) (x != NULL && !evutil_ascii_strcasecmp(x, SHADOWSOCKS)) +void tunnel_ss(struct bufferevent *raw, struct evhttp_connection *wss); + #ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK void ssl_keylog_callback(const SSL *ssl, const char *line); #endif diff --git a/run-test.sh b/run-test.sh index e928381..bf7bcdb 100755 --- a/run-test.sh +++ b/run-test.sh @@ -95,3 +95,15 @@ fi echo "cleaning..." sleep 1 +kill $lpid +sleep 1 +echo wss-proxy client - wss-proxy server - ss +ss-local -l $lport -s 127.0.0.1 -p $sport -m chacha20-ietf-poly1305 -k sip003 --plugin ./wss-proxy-client --plugin-opts "mux=0;ws=0" & +lpid=$! +sleep 1 +if ! check; then + exit 1 +fi + +echo "cleaning..." +sleep 1 diff --git a/wss-proxy-client.c b/wss-proxy-client.c index 76b8378..505ebfb 100644 --- a/wss-proxy-client.c +++ b/wss-proxy-client.c @@ -11,7 +11,8 @@ #include "common.h" struct wss_server_info { - uint8_t tls; + uint8_t tls: 1; + uint8_t ws: 1; uint16_t port; const char *addr; const char *host; @@ -60,6 +61,7 @@ static int init_wss_addr(struct wss_server_info *server) { int port; char *end; int mux = 1; + char *wss; const char *loglevel; const char *remote_host = getenv("SS_REMOTE_HOST"); const char *remote_port = getenv("SS_REMOTE_PORT"); @@ -124,6 +126,14 @@ static int init_wss_addr(struct wss_server_info *server) { mux = (int) strtol(end, NULL, 10); } + // wss + if ((end = strstr(options, "ws=")) != NULL) { + end += 3; + server->ws = (int) strtol(end, NULL, 10); + } else { + server->ws = 1; + } + // strip if ((end = strstr(server->host, ";")) != NULL) { *end = '\0'; @@ -132,8 +142,21 @@ static int init_wss_addr(struct wss_server_info *server) { *end = '\0'; } - LOGI("wss client %s:%d (%s://%s%s)", remote_host, port, server->tls ? "wss" : "ws", server->host, server->path); - if (mux) { + if (server->ws) { + if (server->tls) { + wss = "wss"; + } else { + wss = "ws"; + } + } else { + if (server->tls) { + wss = "sss"; + } else { + wss = "ss"; + } + } + LOGI("wss client %s:%d (%s://%s%s)", remote_host, port, wss, server->host, server->path); + if (server->ws && mux) { LOGW("mux %d is unsupported", mux); } return 0; @@ -159,7 +182,11 @@ static void http_request_cb(struct evhttp_request *req, void *raw) { int status = req == NULL ? -1 : evhttp_request_get_response_code(req); bufferevent_getcb(raw, NULL, NULL, NULL, (void **) &wss); if (status == 101 && is_websocket_handshake(req)) { - tunnel_wss(raw, wss); + if (IS_SHADOWSOCKS(evhttp_find_header(evhttp_request_get_input_headers(req), X_UPGRADE))) { + tunnel_ss(raw, wss); + } else { + tunnel_wss(raw, wss); + } } else { LOGE("wss fail for peer %d, status: %d", get_peer_port(raw), status); bufferevent_free(raw); @@ -240,6 +267,9 @@ static struct evhttp_connection *connect_wss(struct wss_proxy_context *context, #endif evhttp_add_header(output_headers, "Sec-WebSocket-Version", "13"); evhttp_add_header(output_headers, "User-Agent", context->user_agent); + if (!context->server.ws) { + evhttp_add_header(output_headers, X_UPGRADE, SHADOWSOCKS); + } if (evhttp_make_request(wss, req, EVHTTP_REQ_GET, context->server.path)) { LOGE("cannot make http request for peer %d", port); diff --git a/wss-proxy-server.c b/wss-proxy-server.c index 0030c23..47a0239 100644 --- a/wss-proxy-server.c +++ b/wss-proxy-server.c @@ -99,6 +99,7 @@ static void generic_request_handler(struct evhttp_request *req, void *ctx) { struct raw_server_info *raw_server_info = ctx; char sec_websocket_accept[29]; struct evkeyvalq *headers = evhttp_request_get_output_headers(req); + int ss; if (!do_websocket_handshake(req, sec_websocket_accept)) { goto error; @@ -120,9 +121,17 @@ static void generic_request_handler(struct evhttp_request *req, void *ctx) { evhttp_add_header(headers, "Upgrade", "websocket"); evhttp_add_header(headers, "Connection", "Upgrade"); evhttp_add_header(headers, "Sec-WebSocket-Accept", (char *) sec_websocket_accept); + ss = IS_SHADOWSOCKS(evhttp_find_header(evhttp_request_get_input_headers(req), X_UPGRADE)); + if (ss) { + evhttp_add_header(headers, X_UPGRADE, SHADOWSOCKS); + } evhttp_send_reply(req, 101, "Switching Protocols", NULL); - tunnel_wss(raw, wss); + if (ss) { + tunnel_ss(raw, wss); + } else { + tunnel_wss(raw, wss); + } return; error: evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");