diff --git a/CHANGES.md b/CHANGES.md index 171a6b3..f41997b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +v0.2.1 (2015-9-08) +----------- +* Change: Minor fix + + v0.2.0 (2015-5-11) ----------- * Feature: Support UDP relay diff --git a/LICENSE b/LICENSE index 76411d0..6e73c8e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-2015 Ken +Copyright (c) 2014-2015 lparam Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/Makefile b/Makefile index 59a4703..5a5b05c 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,6 @@ -# -# (C) Copyright 2000-2015 -# Ken -# - MAJOR = 0 MINOR = 2 -PATCH = 0 +PATCH = 1 NAME = socksd ifdef O @@ -51,6 +46,7 @@ endif CFLAGS = \ -Os \ + -g \ -std=gnu99 \ -Wall \ $(PLATFORM_CFLAGS) diff --git a/README.md b/README.md index 7e1d9f5..31de2b2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ socksd ================= -a socks5 server implements [RFC 1928](http://tools.ietf.org/html/rfc1928) (SOCKS V5) +A socks5 server implements [RFC 1928](http://tools.ietf.org/html/rfc1928) (SOCKS V5) Features ------------ @@ -12,7 +12,7 @@ Features The MIT License (MIT) -Copyright (c) 2014-2015 Ken +Copyright (c) 2014-2015 lparam Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/config.mk b/config.mk index e603af7..fc57d55 100644 --- a/config.mk +++ b/config.mk @@ -1,9 +1,3 @@ -# -# (C) Copyright 2000-2015 -# Ken -# - -######################################################################### ifneq ($(OBJTREE),$(SRCTREE)) ifeq ($(CURDIR),$(SRCTREE)) @@ -81,4 +75,3 @@ endif $(obj)%.o: %.c $(shell [ -d $(dir $@) ] || mkdir -p $(dir $@)) $(CCC) -c $< -o $(cobj) - diff --git a/src/client.c b/src/client.c index 596bfab..a14af37 100644 --- a/src/client.c +++ b/src/client.c @@ -56,15 +56,19 @@ verify_request(char *buf, ssize_t buflen) { struct socks5_request *req = (struct socks5_request *)buf; if (req->atyp == S5_ATYP_IPV4) { - if ((req->cmd == S5_CMD_CONNECT) && (strncmp(buf + 4, "\x0\x0\x0\x0", 4) == 0)) { + if ((req->cmd == S5_CMD_CONNECT) + && (strncmp(buf + 4, "\x0\x0\x0\x0", 4) == 0)) { return 0; } len = sizeof(struct socks5_request) + sizeof(struct in_addr) + 2; + } else if (req->atyp == S5_ATYP_HOST) { uint8_t name_len = *(uint8_t *)(req->addr); len = sizeof(struct socks5_request) + 1 + name_len + 2; + } else if (req->atyp == S5_ATYP_IPV6) { len = sizeof(struct socks5_request) + sizeof(struct in6_addr) + 2; + } else { len = 0; } @@ -80,24 +84,28 @@ analyse_request_addr(struct socks5_request *req, struct sockaddr *dest, char *de struct sockaddr_in6 addr6; } addr; int addrlen; - uint16_t portlen = 2; // network byte order port number, 2 bytes + /* network byte order port number, 2 bytes */ + uint16_t portlen = 2; memset(&addr, 0, sizeof(addr)); if (req->atyp == S5_ATYP_IPV4) { - size_t in_addr_len = sizeof(struct in_addr); // 4 bytes for IPv4 address + /* 4 bytes for IPv4 address */ + size_t in_addr_len = sizeof(struct in_addr); addr.addr4.sin_family = AF_INET; memcpy(&addr.addr4.sin_addr, req->addr, in_addr_len); memcpy(&addr.addr4.sin_port, req->addr + in_addr_len, portlen); - uv_inet_ntop(AF_INET, (const void *)(req->addr), dest_buf, INET_ADDRSTRLEN); + uv_inet_ntop(AF_INET, (const void *)(req->addr), dest_buf, + INET_ADDRSTRLEN); uint16_t port = read_size((uint8_t*)(req->addr + in_addr_len)); sprintf(dest_buf, "%s:%u", dest_buf, port); addrlen = 4; } else if (req->atyp == S5_ATYP_HOST) { - uint8_t namelen = *(uint8_t *)(req->addr); // 1 byte of name length + /* 1 byte of name length */ + uint8_t namelen = *(uint8_t *)(req->addr); if (namelen > 0xFF) { return 0; } @@ -112,11 +120,13 @@ analyse_request_addr(struct socks5_request *req, struct sockaddr *dest, char *de addrlen = 1 + namelen; } else if (req->atyp == S5_ATYP_IPV6) { - size_t in6_addr_len = sizeof(struct in6_addr); // 16 bytes for IPv6 address + /* 16 bytes for IPv6 address */ + size_t in6_addr_len = sizeof(struct in6_addr); memcpy(&addr.addr6.sin6_addr, req->addr, in6_addr_len); memcpy(&addr.addr6.sin6_port, req->addr + in6_addr_len, portlen); - uv_inet_ntop(AF_INET6, (const void *)(req->addr), dest_buf, INET_ADDRSTRLEN); + uv_inet_ntop(AF_INET6, (const void *)(req->addr), dest_buf, + INET_ADDRSTRLEN); uint16_t port = read_size((uint8_t*)(req->addr + in6_addr_len)); sprintf(dest_buf, "%s:%u", dest_buf, port); @@ -136,7 +146,8 @@ send_to_client(struct client_context *client, char *buf, int buflen) { client->write_req.data = client; uv_write_t *write_req = malloc(sizeof(*write_req)); write_req->data = client; - int rc = uv_write(write_req, &client->handle.stream, &reply, 1, client_send_cb); + int rc = uv_write(write_req, &client->handle.stream, &reply, 1, + client_send_cb); if (rc) { logger_log(LOG_ERR, "write to client error: %s", uv_strerror(rc)); } @@ -166,17 +177,23 @@ request_ack(struct client_context *client, enum s5_rep rep) { buf[2] = 0x00; // RSV memset(&addr, 0, sizeof(addr)); + if (client->cmd == S5_CMD_UDP_ASSOCIATE) { - uv_tcp_getsockname(&client->handle.tcp, (struct sockaddr *) &addr, &addrlen); + uv_tcp_getsockname(&client->handle.tcp, (struct sockaddr *) &addr, + &addrlen); + } else { - uv_tcp_getsockname(&remote->handle.tcp, (struct sockaddr *) &addr, &addrlen); + uv_tcp_getsockname(&remote->handle.tcp, (struct sockaddr *) &addr, + &addrlen); } + if (addrlen == sizeof(struct sockaddr_in6)) { buf[3] = 0x04; /* atyp - IPv6. */ const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)&addr; memcpy(buf + 4, &addr6->sin6_addr, 16); /* BND.ADDR */ memcpy(buf + 20, &addr6->sin6_port, 2); /* BND.PORT */ buflen = 22; + } else { buf[3] = 0x01; /* atyp - IPv4. */ const struct sockaddr_in *addr4 = (const struct sockaddr_in *)&addr; @@ -191,6 +208,7 @@ request_ack(struct client_context *client, enum s5_rep rep) { } else { client->stage = S5_STAGE_UDP_RELAY; } + } else { client->stage = S5_STAGE_TERMINATE; } @@ -221,7 +239,8 @@ request_start(struct client_context *client, char *buf, ssize_t buflen) { client->cmd = request->cmd; - if (request->cmd != S5_CMD_CONNECT && request->cmd != S5_CMD_UDP_ASSOCIATE) { + if (request->cmd != S5_CMD_CONNECT && + request->cmd != S5_CMD_UDP_ASSOCIATE) { logger_log(LOG_ERR, "unsupported cmd: 0x%02x", request->cmd); request_ack(client, S5_REP_CMD_NOT_SUPPORTED); return; @@ -233,7 +252,8 @@ request_start(struct client_context *client, char *buf, ssize_t buflen) { } char host[256] = {0}; - int addrlen = analyse_request_addr(request, &remote->addr, client->target_addr, host); + int addrlen = analyse_request_addr(request, &remote->addr, + client->target_addr, host); if (addrlen < 1) { logger_log(LOG_ERR, "unsupported address type: 0x%02x", request->atyp); request_ack(client, S5_REP_ADDRESS_TYPE_NOT_SUPPORTED); @@ -274,6 +294,7 @@ client_send_cb(uv_write_t *req, int status) { if (client->stage == S5_STAGE_FORWARD) { reset_timer(remote); receive_from_remote(remote); + } else if (client->stage == S5_STAGE_TERMINATE) { close_client(client); close_remote(remote); @@ -282,7 +303,8 @@ client_send_cb(uv_write_t *req, int status) { } else { char addrbuf[INET6_ADDRSTRLEN + 1] = {0}; uint16_t port = ip_name(&client->addr, addrbuf, sizeof addrbuf); - logger_log(LOG_ERR, "%s -> %s:%d failed: %s", client->target_addr, addrbuf, port, uv_strerror(status)); + logger_log(LOG_ERR, "%s:%d <- %s failed: %s", addrbuf, port, + client->target_addr, uv_strerror(status)); } free(req); @@ -295,6 +317,7 @@ client_recv_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { if (nread > 0) { switch (client->stage) { + case S5_STAGE_HANDSHAKE: if (verify_methods(buf->base, nread)) { handshake(client); @@ -304,6 +327,7 @@ client_recv_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { close_remote(remote); } break; + case S5_STAGE_REQUEST: if (verify_request(buf->base, nread)) { request_start(client, buf->base, nread); @@ -313,10 +337,12 @@ client_recv_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { close_remote(remote); } break; + case S5_STAGE_FORWARD: uv_read_stop(&client->handle.stream); forward_to_remote(remote, buf->base, nread); break; + default: break; } @@ -325,7 +351,8 @@ client_recv_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { if (nread != UV_EOF) { char addrbuf[INET6_ADDRSTRLEN + 1] = {0}; uint16_t port = ip_name(&client->addr, addrbuf, sizeof addrbuf); - logger_log(LOG_ERR, "receive from %s:%d failed: %s", addrbuf, port, uv_strerror(nread)); + logger_log(LOG_ERR, "receive from %s:%d failed: %s", addrbuf, port, + uv_strerror(nread)); } close_client(client); close_remote(remote); @@ -350,7 +377,8 @@ client_accept_cb(uv_stream_t *server, int status) { uv_tcp_getpeername(&client->handle.tcp, &client->addr, &namelen); reset_timer(remote); client->handle.stream.data = client; - rc = uv_read_start(&client->handle.stream, client_alloc_cb, client_recv_cb); + rc = uv_read_start(&client->handle.stream, client_alloc_cb, + client_recv_cb); } else { logger_log(LOG_ERR, "accept error: %s", uv_strerror(rc)); close_client(client); diff --git a/src/consumer.c b/src/consumer.c index f9b4ab6..18e31a2 100644 --- a/src/consumer.c +++ b/src/consumer.c @@ -5,7 +5,6 @@ #include #include "uv.h" - #include "util.h" #include "common.h" #include "udprelay.h" @@ -13,6 +12,7 @@ #include "consumer.h" +extern uv_key_t thread_resolver_key; extern void close_loop(uv_loop_t *loop); static void @@ -67,8 +67,9 @@ consumer_close(uv_async_t *handle) { udprelay_close(ctx); } - struct resolver_context *res = handle->loop->data; - resolver_shutdown(res); + struct resolver_context *dns = uv_key_get(&thread_resolver_key); + assert(dns != NULL); + resolver_shutdown(dns); } static void @@ -108,9 +109,11 @@ consumer_start(void *arg) { get_listen_handle(loop, (uv_stream_t*)&ctx->server_handle); - struct resolver_context *res = resolver_init(loop, MODE_IPV4, - ctx->nameserver_num == 0 ? NULL : ctx->nameservers, ctx->nameserver_num); - loop->data = res; + struct resolver_context *dns = + resolver_init(loop, MODE_IPV4, + ctx->nameserver_num == 0 ? NULL : ctx->nameservers, ctx->nameserver_num); + + uv_key_set(&thread_resolver_key, dns); uv_listen((uv_stream_t*)&ctx->server_handle, 128, ctx->accept_cb); @@ -122,7 +125,7 @@ consumer_start(void *arg) { close_loop(loop); free(loop); - resolver_destroy(res); + resolver_destroy(dns); uv_sem_post(&ctx->semaphore); } diff --git a/src/main.c b/src/main.c index 1759909..0b2892e 100644 --- a/src/main.c +++ b/src/main.c @@ -45,7 +45,7 @@ static const struct option _lopts[] = { static void print_usage(const char *prog) { - printf("socksd Version: %s Maintained by Ken \n", SOCKSD_VER); + printf("socksd Version: %s Maintained by lparam\n", SOCKSD_VER); printf("Usage: %s [-l bind] [-p pidfile] [-c concurrency] [-t timeout] -s [signal] [-nhvV]\n\n", prog); printf("Options:\n"); puts(" -h, --help\t\t : this help\n" @@ -67,32 +67,41 @@ parse_opts(int argc, char *argv[]) { int opt = 0, longindex = 0; while ((opt = getopt_long(argc, argv, _optString, _lopts, &longindex)) != -1) { + switch (opt) { + case 'v': printf("socksd version: %s \n", SOCKSD_VER); exit(0); break; + case 'h': case '?': print_usage(argv[0]); break; + case 'l': local_addrbuf = optarg; break; + case 'c': concurrency = strtol(optarg, NULL, 10); break; + case 'd': if (nameserver_num < MAX_DNS_NUM) { nameservers[nameserver_num++] = optarg; } break; + case 'p': pidfile = optarg; break; + case 'n': daemon_mode = 0; break; + case 's': xsignal = optarg; if (strcmp(xsignal, "stop") == 0 @@ -102,12 +111,15 @@ parse_opts(int argc, char *argv[]) { fprintf(stderr, "invalid option: -s %s\n", xsignal); print_usage(argv[0]); break; + case 't': idle_timeout = strtol(optarg, NULL, 10); break; + case 'V': verbose = 1; break; + default: print_usage(argv[0]); break; @@ -138,8 +150,8 @@ signal_cb(uv_signal_t *handle, int signum) { uv_signal_stop(&signals[i].sig); } - struct resolver_context *res = handle->loop->data; - resolver_shutdown(res); + struct resolver_context *dns = uv_key_get(&thread_resolver_key); + resolver_shutdown(dns); struct server_context *ctx = handle->data; uv_close((uv_handle_t *)&ctx->tcp, NULL); udprelay_close(ctx); @@ -172,6 +184,7 @@ init(void) { signal(SIGPIPE, SIG_IGN); resolver_prepare(nameserver_num); + uv_key_create(&thread_resolver_key); if (idle_timeout == 0) { idle_timeout = 60; @@ -225,16 +238,17 @@ main(int argc, char *argv[]) { setup_signal(loop, signal_cb, &ctx); - struct resolver_context *res = resolver_init(loop, MODE_IPV4, - nameserver_num == 0 ? NULL : nameservers, nameserver_num); - loop->data = res; + struct resolver_context *dns = + resolver_init(loop, MODE_IPV4, + nameserver_num == 0 ? NULL : nameservers, nameserver_num); + uv_key_set(&thread_resolver_key, dns); udprelay_start(loop, &ctx); uv_run(loop, UV_RUN_DEFAULT); close_loop(loop); - resolver_destroy(res); + resolver_destroy(dns); } else { logger_stderr("listen error: %s", uv_strerror(rc)); @@ -274,6 +288,8 @@ main(int argc, char *argv[]) { udprelay_destroy(); + uv_key_delete(&thread_resolver_key); + if (daemon_mode) { delete_pidfile(pidfile); } diff --git a/src/remote.c b/src/remote.c index 74b605e..f11ac64 100644 --- a/src/remote.c +++ b/src/remote.c @@ -21,9 +21,14 @@ remote_timer_expire(uv_timer_t *handle) { struct remote_context *remote = handle->data; struct client_context *client = remote->client; if (verbose) { - char addrbuf[INET6_ADDRSTRLEN + 1] = {0}; - uint16_t port = ip_name(&client->addr, addrbuf, sizeof addrbuf); - logger_log(LOG_WARNING, "%s:%d <-> %s connection timeout", addrbuf, port, client->target_addr); + if (client->cmd == S5_CMD_UDP_ASSOCIATE) { + logger_log(LOG_WARNING, "udp assocation timeout"); + } else { + char addrbuf[INET6_ADDRSTRLEN + 1] = {0}; + uint16_t port = ip_name(&client->addr, addrbuf, sizeof addrbuf); + logger_log(LOG_WARNING, "%s:%d <-> %s connection timeout", addrbuf, + port, client->target_addr); + } } request_ack(remote->client, S5_REP_TTL_EXPIRED); } @@ -32,7 +37,7 @@ void reset_timer(struct remote_context *remote) { if (remote->timer != NULL) { remote->timer->data = remote; - uv_timer_start(remote->timer, remote_timer_expire, remote->idle_timeout, 0); + uv_timer_start(remote->timer, remote_timer_expire, remote->idle_timeout * 1000, 0); } } @@ -46,7 +51,7 @@ new_remote(uint16_t timeout) { struct remote_context *remote = malloc(sizeof(*remote)); memset(remote, 0, sizeof(*remote)); remote->timer = malloc(sizeof(uv_timer_t)); - remote->idle_timeout = timeout * 1000; + remote->idle_timeout = timeout; return remote; } @@ -101,9 +106,11 @@ remote_connect_cb(uv_connect_t *req, int status) { request_ack(client, S5_REP_SUCCESSED); remote->handle.stream.data = remote; uv_read_start(&remote->handle.stream, remote_alloc_cb, remote_recv_cb); + } else { if (status != UV_ECANCELED) { - logger_log(LOG_ERR, "connect to %s failed: %s", client->target_addr, uv_strerror(status)); + logger_log(LOG_ERR, "connect to %s failed: %s", client->target_addr, + uv_strerror(status)); request_ack(client, S5_REP_HOST_UNREACHABLE); } } @@ -119,16 +126,19 @@ void forward_to_remote(struct remote_context *remote, char *buf, int buflen) { uv_buf_t request = uv_buf_init(buf, buflen); remote->write_req.data = remote; - uv_write(&remote->write_req, &remote->handle.stream, &request, 1, remote_send_cb); + uv_write(&remote->write_req, &remote->handle.stream, &request, 1, + remote_send_cb); } void connect_to_remote(struct remote_context *remote) { remote->stage = S5_STAGE_CONNECT; remote->connect_req.data = remote; - int rc = uv_tcp_connect(&remote->connect_req, &remote->handle.tcp, &remote->addr, remote_connect_cb); + int rc = uv_tcp_connect(&remote->connect_req, &remote->handle.tcp, + &remote->addr, remote_connect_cb); if (rc) { - logger_log(LOG_ERR, "connect to %s error: %s", remote->client->target_addr, uv_strerror(rc)); + logger_log(LOG_ERR, "connect to %s error: %s", + remote->client->target_addr, uv_strerror(rc)); request_ack(remote->client, S5_REP_NETWORK_UNREACHABLE); } } @@ -139,6 +149,8 @@ resolve_cb(struct sockaddr *addr, void *data) { struct client_context *client = remote->client; if (addr == NULL) { + logger_log(LOG_ERR, "resolve %s failed: %s", + remote->client->target_addr, resolver_error(remote->addr_query)); remote->stage = S5_STAGE_TERMINATE; request_ack(client, S5_REP_HOST_UNREACHABLE); @@ -153,9 +165,16 @@ resolve_cb(struct sockaddr *addr, void *data) { void resolve_remote(struct remote_context *remote, char *addr, uint16_t port) { - struct resolver_context *ctx = remote->handle.handle.loop->data; + if (verbose) { + logger_log(LOG_INFO, "resolve %s", addr); + } + struct resolver_context *ctx = uv_key_get(&thread_resolver_key); remote->stage = S5_STAGE_RESOLVE; remote->addr_query = resolver_query(ctx, addr, port, resolve_cb, remote); + if (remote->addr_query == NULL) { + remote->stage = S5_STAGE_TERMINATE; + request_ack(remote->client, S5_REP_HOST_UNREACHABLE); + } } static void @@ -171,7 +190,8 @@ remote_send_cb(uv_write_t *req, int status) { if (verbose) { char addrbuf[INET6_ADDRSTRLEN + 1] = {0}; uint16_t port = ip_name(&client->addr, addrbuf, sizeof addrbuf); - logger_log(LOG_ERR, "%s:%d -> failed: %s", addrbuf, port, client->target_addr, uv_strerror(status)); + logger_log(LOG_ERR, "%s:%d -> failed: %s", addrbuf, port, + client->target_addr, uv_strerror(status)); } } } @@ -187,9 +207,11 @@ remote_recv_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { if (nread > 0) { uv_read_stop(&remote->handle.stream); forward_to_client(client, buf->base, nread); + } else if (nread < 0){ if (nread != UV_EOF && verbose) { - logger_log(LOG_ERR, "receive from %s failed: %s", client->target_addr, uv_strerror(nread)); + logger_log(LOG_ERR, "receive from %s failed: %s", + client->target_addr, uv_strerror(nread)); } close_client(client); close_remote(remote); diff --git a/src/resolver.c b/src/resolver.c index d20b4d3..c927db7 100644 --- a/src/resolver.c +++ b/src/resolver.c @@ -87,15 +87,13 @@ static void dns_query_a4_cb(struct dns_ctx *dns, struct dns_rr_a4 *result, void *data) { struct dns_query *query = (struct dns_query *)data; - if (result == NULL) { - if (verbose) { - logger_log(LOG_ERR, "IPv4 resolver: %s", dns_strerror(dns_status(dns))); - } - } else if (result->dnsa4_nrr > 0) { - query->responses = realloc(query->responses, (query->response_count + result->dnsa4_nrr) * sizeof(struct sockaddr *)); + if (result != NULL && result->dnsa4_nrr > 0) { + query->responses = realloc(query->responses, + (query->response_count + result->dnsa4_nrr) * sizeof(struct sockaddr *)); query->response_count = result->dnsa4_nrr; for (int i = 0; i < result->dnsa4_nrr; i++) { struct sockaddr_in *sa = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in)); + memset(sa, 0, sizeof(struct sockaddr_in)); sa->sin_family = AF_INET; sa->sin_addr = result->dnsa4_addr[i]; sa->sin_port = query->port; @@ -115,15 +113,12 @@ static void dns_query_a6_cb(struct dns_ctx *dns, struct dns_rr_a6 *result, void *data) { struct dns_query *query = (struct dns_query *)data; - if (result == NULL) { - if (verbose) { - logger_log(LOG_ERR, "IPv6 resolver: %s", dns_strerror(dns_status(dns))); - } - } else if (result->dnsa6_nrr > 0) { + if (result != NULL && result->dnsa6_nrr > 0) { query->responses = realloc(query->responses, (query->response_count + result->dnsa6_nrr) * sizeof(struct sockaddr *)); query->response_count = result->dnsa6_nrr; for (int i = 0; i < result->dnsa6_nrr; i++) { struct sockaddr_in6 *sa = (struct sockaddr_in6 *)malloc(sizeof(struct sockaddr_in6)); + memset(sa, 0, sizeof(struct sockaddr_in6)); sa->sin6_family = AF_INET6; sa->sin6_addr = result->dnsa6_addr[i]; sa->sin6_port = query->port; @@ -274,3 +269,8 @@ resolver_cancel(struct dns_query *query) { free(query); } + +const char* +resolver_error(struct dns_query *query) { + return dns_strerror(dns_status(query->context->dns)); +} diff --git a/src/resolver.h b/src/resolver.h index 3f79138..8203961 100644 --- a/src/resolver.h +++ b/src/resolver.h @@ -24,5 +24,6 @@ struct dns_query * resolver_query(struct resolver_context *ctx, const char *host void resolver_cancel(struct dns_query *); void resolver_shutdown(struct resolver_context *rctx); void resolver_destroy(struct resolver_context *ctx); +const char* resolver_error(struct dns_query *query); #endif // for #ifndef _RESOLVER_H diff --git a/src/socksd.h b/src/socksd.h index 5177516..254988b 100644 --- a/src/socksd.h +++ b/src/socksd.h @@ -7,7 +7,7 @@ #include "resolver.h" -#define SOCKSD_VERSION "0.2.0" +#define SOCKSD_VERSION "0.2.1" #define SOCKSD_VER "socksd/" SOCKSD_VERSION @@ -64,5 +64,6 @@ void close_loop(uv_loop_t *loop); int verbose; uint16_t idle_timeout; +uv_key_t thread_resolver_key; #endif // for #ifndef _SOCKSD_H diff --git a/src/udprelay.c b/src/udprelay.c index 0318aaf..0e58637 100644 --- a/src/udprelay.c +++ b/src/udprelay.c @@ -22,7 +22,7 @@ struct target_context { struct sockaddr dest_addr; uint16_t dest_port; uv_timer_t *timer; - struct dns_query *addr_query; + struct dns_query *host_query; int header_len; uint8_t *buf; ssize_t buflen; @@ -31,6 +31,7 @@ struct target_context { extern int verbose; extern uint16_t idle_timeout; +extern uv_key_t thread_resolver_key; static uv_mutex_t mutex; static struct cache *cache; @@ -79,54 +80,10 @@ close_target(struct target_context *target) { } } -static int -parse_target_address(const uint8_t atyp, const char *addrbuf, struct sockaddr *addr, char *host) { - int addrlen; - uint16_t portlen = 2; // network byte order port number, 2 bytes - union { - struct sockaddr addr; - struct sockaddr_in addr4; - struct sockaddr_in6 addr6; - } dest; - - memset(&dest, 0, sizeof(dest)); - - if (atyp == S5_ATYP_IPV4) { - size_t in_addr_len = sizeof(struct in_addr); // 4 bytes for IPv4 address - dest.addr4.sin_family = AF_INET; - memcpy(&dest.addr4.sin_addr, addrbuf, in_addr_len); - memcpy(&dest.addr4.sin_port, addrbuf + in_addr_len, portlen); - addrlen = 4 + portlen; - - } else if (atyp == S5_ATYP_HOST) { - uint8_t namelen = *(uint8_t *)(addrbuf); // 1 byte of name length - if (namelen > 0xFF) { - return -1; - } - memcpy(&dest.addr4.sin_port, addrbuf + 1 + namelen, portlen); - memcpy(host, addrbuf + 1, namelen); - host[namelen] = '\0'; - addrlen = 1 + namelen + portlen; - - } else if (atyp == S5_ATYP_IPV6) { - size_t in6_addr_len = sizeof(struct in6_addr); // 16 bytes for IPv6 address - memcpy(&dest.addr6.sin6_addr, addrbuf, in6_addr_len); - memcpy(&dest.addr6.sin6_port, addrbuf + in6_addr_len, portlen); - addrlen = 16 + portlen; - - } else { - return 0; - } - - memcpy(addr, &dest.addr, sizeof(*addr)); - return addrlen; -} - static void target_alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { struct target_context *target = handle->data; buf->base = malloc(suggested_size) + target->header_len; - memset(buf->base - target->header_len, 0, suggested_size); buf->len = suggested_size - target->header_len; } @@ -193,7 +150,7 @@ target_recv_cb(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const struc uint16_t src_port = 0, dst_port = 0; src_port = ip_name(addr, src, sizeof src); dst_port = ip_name(&target->client_addr, dst, sizeof dst); - logger_log(LOG_INFO, "%s:%d -> %s:%d", src, src_port, dst, dst_port); + logger_log(LOG_INFO, "%s:%d <- %s:%d", dst, dst_port, src, src_port); } forward_to_client(target, m, mlen); @@ -210,6 +167,7 @@ target_send_cb(uv_udp_send_t *req, int status) { logger_log(LOG_ERR, "forward to target failed: %s", uv_strerror(status)); } uv_buf_t *buf = (uv_buf_t *)(req + 1); + // free client recv buffer free(buf->base); free(req); } @@ -224,7 +182,9 @@ forward_to_target(struct target_context *target, uint8_t *data, ssize_t len) { dst_port = ip_name(&target->dest_addr, dst, sizeof dst); logger_log(LOG_INFO, "%s:%d -> %s:%d", src, src_port, dst, dst_port); } - uv_udp_send_t *write_req = malloc(sizeof(*write_req) + sizeof(uv_buf_t)); + + ssize_t sz = sizeof(uv_udp_send_t) + sizeof(uv_buf_t); + uv_udp_send_t *write_req = malloc(sz); uv_buf_t *buf = (uv_buf_t *)(write_req + 1); buf->base = (char *)data; buf->len = len; @@ -239,16 +199,85 @@ resolve_cb(struct sockaddr *addr, void *data) { target->header_len = addr->sa_family == AF_INET ? IPV4_HEADER_LEN : IPV6_HEADER_LEN; target->dest_addr = *addr; forward_to_target(target, target->buf, target->buflen); + target->buf = NULL; + target->buflen = 0; } else { - logger_stderr("resolve failed."); + free(target->buf); + logger_log(LOG_ERR, "[udp] resolve failed: %s", resolver_error(target->host_query)); } } static void resolve_target(struct target_context *target, char *addr, uint16_t port) { - struct resolver_context *ctx = target->server_handle->loop->data; - target->addr_query = resolver_query(ctx, addr, port, resolve_cb, target); + if (verbose) { + logger_log(LOG_INFO, "resolve %s", addr); + } + struct resolver_context *ctx = uv_key_get(&thread_resolver_key); + target->host_query = resolver_query(ctx, addr, port, resolve_cb, target); +} + +static int +parse_target_address(const uint8_t atyp, const char *addrbuf, struct sockaddr *addr, char *host) { + int addrlen; + uint16_t portlen = 2; // network byte order port number, 2 bytes + union { + struct sockaddr addr; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + } dest; + + memset(&dest, 0, sizeof(dest)); + + if (atyp == S5_ATYP_IPV4) { + size_t in_addr_len = sizeof(struct in_addr); // 4 bytes for IPv4 address + dest.addr4.sin_family = AF_INET; + memcpy(&dest.addr4.sin_addr, addrbuf, in_addr_len); + memcpy(&dest.addr4.sin_port, addrbuf + in_addr_len, portlen); + addrlen = 4 + portlen; + + } else if (atyp == S5_ATYP_HOST) { + uint8_t namelen = *(uint8_t *)(addrbuf); // 1 byte of name length + if (namelen > 0xFF) { + return -1; + } + memcpy(&dest.addr4.sin_port, addrbuf + 1 + namelen, portlen); + memcpy(host, addrbuf + 1, namelen); + host[namelen] = '\0'; + addrlen = 1 + namelen + portlen; + + } else if (atyp == S5_ATYP_IPV6) { + size_t in6_addr_len = sizeof(struct in6_addr); // 16 bytes for IPv6 address + memcpy(&dest.addr6.sin6_addr, addrbuf, in6_addr_len); + memcpy(&dest.addr6.sin6_port, addrbuf + in6_addr_len, portlen); + addrlen = 16 + portlen; + + } else { + return 0; + } + + memcpy(addr, &dest.addr, sizeof(*addr)); + return addrlen; +} + +static void +cache_log(uint8_t atyp, const struct sockaddr *src_addr, const struct sockaddr *dst_addr, + const char *host, uint16_t port, int hit) { + char src[INET6_ADDRSTRLEN + 1] = {0}; + char dst[INET6_ADDRSTRLEN + 1] = {0}; + uint16_t src_port = 0, dst_port = 0; + char *hint = hit ? "hit" : "miss"; + if (verbose) { + src_port = ip_name(src_addr, src, sizeof src); + if (atyp == S5_ATYP_HOST) { + logger_log(hint ? LOG_INFO : LOG_WARNING, "[udp] cache %s: %s:%d -> %s:%d", + hint, src, src_port, host, ntohs(port)); + } else { + dst_port = ip_name(dst_addr, dst, sizeof dst); + logger_log(hint ? LOG_INFO : LOG_WARNING, "[udp] cache %s: %s:%d -> %s:%d", + hint, src, src_port, dst, dst_port); + } + } } /* @@ -271,6 +300,7 @@ client_recv_cb(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const struc logger_log(LOG_ERR, "don't support frag: %d", frag); goto err; } + memset(&dest_addr, 0, sizeof(dest_addr)); uint8_t atyp = (uint8_t)buf->base[3]; int addrlen = parse_target_address(atyp, buf->base + 4, &dest_addr, host); if (addrlen < 1) { @@ -278,6 +308,8 @@ client_recv_cb(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const struc goto err; } + uint16_t port = (*(uint16_t *)(buf->base + 4 + addrlen - 2)); + char key[KEY_BYTES + 1] = {0}; md5((char*)addr, sizeof(*addr), key); @@ -286,6 +318,8 @@ client_recv_cb(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const struc cache_lookup(cache, key, (void *)&target); uv_mutex_unlock(&mutex); if (target == NULL) { + cache_log(atyp, addr, &dest_addr, host, port, 0); + target = new_target(); target->client_addr = *addr; target->server_handle = handle; @@ -303,11 +337,12 @@ client_recv_cb(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const struc uv_mutex_lock(&mutex); cache_insert(cache, target->key, (void *)target); uv_mutex_unlock(&mutex); + + } else { + cache_log(atyp, addr, &dest_addr, host, port, 1); } - target->dest_addr = dest_addr; reset_timer(target); - uint16_t port = (*(uint16_t *)(buf->base + 4 + addrlen - 2)); uint8_t *m = (uint8_t*)buf->base; ssize_t mlen = nread - 4 - addrlen; memmove(m, m + 4 + addrlen, mlen); @@ -316,6 +351,7 @@ client_recv_cb(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const struc case S5_ATYP_IPV4: case S5_ATYP_IPV6: + target->dest_addr = dest_addr; target->header_len = dest_addr.sa_family == AF_INET ? IPV4_HEADER_LEN : IPV6_HEADER_LEN; forward_to_target(target, m, mlen); break;