| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- /*
- * socks5 proxy server
- *
- * @build: make examples
- *
- * @proxy_server bin/socks5_proxy_server 1080
- * bin/socks5_proxy_server 1080 username password
- *
- * @proxy_client curl -v http://www.example.com/ --proxy socks5://127.0.0.1:1080
- * curl -v http://www.example.com/ --proxy socks5://username:password@127.0.0.1:1080
- *
- */
- #include "hv.h"
- #include "hloop.h"
- static char proxy_host[64] = "0.0.0.0";
- static int proxy_port = 1080;
- static const char* auth_username = NULL;
- static const char* auth_password = NULL;
- #define SOCKS5_VERSION ((uint8_t)5)
- #define SOCKS5_AUTH_VERSION ((uint8_t)1)
- #define SOCKS5_AUTH_SUCCESS ((uint8_t)0)
- #define SOCKS5_AUTH_FAILURE ((uint8_t)1)
- typedef enum {
- NoAuth = 0,
- GssApiAuth = 1,
- UserPassAuth = 2,
- } socks5_auth_method;
- typedef enum {
- ConnectCommand = 1,
- BindCommand = 2,
- AssociateCommand= 3,
- } socks5_command;
- typedef enum {
- IPv4Addr = 1,
- FqdnAddr = 3,
- IPv6Addr = 4,
- } socks5_addr_type;
- typedef enum {
- SuccessReply = 0,
- ServerFailure = 1,
- RuleFailure = 2,
- NetworkUnreachable = 3,
- HostUnreachable = 4,
- ConnectRefused = 5,
- TtlExpired = 6,
- CommandNotSupported = 7,
- AddrTypeNotSupported= 8,
- } socks5_reply_code;
- typedef enum {
- s_begin,
- s_auth_methods_count,
- s_auth_methods,
- s_auth_username_len,
- s_auth_username,
- s_auth_password_len,
- s_auth_password,
- s_request,
- s_dst_addr_type,
- s_dst_addr_len,
- s_dst_addr,
- s_dst_port,
- s_upstream,
- s_end,
- } socks5_state_e;
- typedef struct {
- hio_t* io;
- socks5_state_e state;
- socks5_addr_type addr_type;
- sockaddr_u addr;
- } socks5_conn_t;
- /*
- * workflow:
- * hloop_new -> hloop_create_tcp_server -> hloop_run
- * on_accept -> HV_ALLOC(socks5_conn_t) -> hio_readbytes(s_auth_methods_count:2) ->
- * on_recv -> hio_readbytes(s_auth_methods) ->
- * on_recv -> hio_write(auth_method) -> hio_readbytes(s_auth_username_len:2) ->
- * on_recv -> hio_readbytes(s_auth_username) ->
- * on_recv -> hio_readbytes(s_auth_password_len:1) ->
- * on_recv -> hio_readbytes(s_auth_password) -> hio_write(auth_result) ->
- * on_recv -> hio_readbytes(s_request:3) ->
- * on_recv -> hio_readbytes(addr_type:1) ->
- * on_recv -> hio_readbytes(addr_len:1) ->
- * on_recv -> hio_readbytes(addr) ->
- * on_recv -> hio_readbytes(port:2) ->
- * on_recv -> hio_setup_tcp_upstream(io, addr, port) ->
- * on_close -> hio_close_upstream -> HV_FREE(socks5_conn_t)
- *
- */
- static void on_upstream_connect(hio_t* upstream_io) {
- // printf("on_upstream_connect connfd=%d\n", hio_fd(upstream_io));
- socks5_conn_t* conn = (socks5_conn_t*)hevent_userdata(upstream_io);
- sockaddr_u* localaddr = (sockaddr_u*)hio_localaddr(upstream_io);
- uint8_t resp[32] = { SOCKS5_VERSION, SuccessReply, 0, IPv4Addr, 127,0,0,1, 0,80, 0 };
- int resp_len = 3;
- if (localaddr->sa.sa_family == AF_INET) {
- resp[resp_len++] = IPv4Addr;
- memcpy(resp + resp_len, &localaddr->sin.sin_addr, 4); resp_len += 4;
- memcpy(resp + resp_len, &localaddr->sin.sin_port, 2); resp_len += 2;
- } else if (localaddr->sa.sa_family == AF_INET6) {
- resp[resp_len++] = IPv6Addr;
- memcpy(resp + resp_len, &localaddr->sin6.sin6_addr, 16); resp_len += 16;
- memcpy(resp + resp_len, &localaddr->sin6.sin6_port, 2); resp_len += 2;
- }
- hio_write(conn->io, resp, resp_len);
- hio_setcb_read(upstream_io, hio_write_upstream);
- hio_setcb_read(conn->io, hio_write_upstream);
- hio_read(conn->io);
- hio_read(upstream_io);
- }
- static void on_close(hio_t* io) {
- // printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io));
- socks5_conn_t* conn = (socks5_conn_t*)hevent_userdata(io);
- if (conn) {
- hevent_set_userdata(io, NULL);
- HV_FREE(conn);
- }
- hio_close_upstream(io);
- }
- static void on_recv(hio_t* io, void* buf, int readbytes) {
- socks5_conn_t* conn = (socks5_conn_t*)hevent_userdata(io);
- const uint8_t* bytes = (uint8_t*)buf;
- switch(conn->state) {
- case s_begin:
- // printf("s_begin\n");
- conn->state = s_auth_methods_count;
- case s_auth_methods_count:
- // printf("s_auth_methods_count\n");
- {
- assert(readbytes == 2);
- uint8_t version = bytes[0];
- uint8_t methods_count = bytes[1];
- if (version != SOCKS5_VERSION || methods_count == 0) {
- fprintf(stderr, "Unsupprted socks version: %d\n", (int)version);
- hio_close(io);
- return;
- }
- conn->state = s_auth_methods;
- hio_readbytes(io, methods_count);
- }
- break;
- case s_auth_methods:
- // printf("s_auth_methods\n");
- {
- // TODO: check auth methos
- uint8_t auth_method = NoAuth;
- if (auth_username && auth_password) {
- auth_method = UserPassAuth;
- } else {
- // TODO: Implement more auth methods
- }
- // send auth mothod
- uint8_t resp[2] = { SOCKS5_VERSION, NoAuth };
- resp[1] = auth_method;
- hio_write(io, resp, 2);
- if (auth_method == NoAuth) {
- conn->state = s_request;
- hio_readbytes(io, 3);
- } else if (auth_method == UserPassAuth) {
- conn->state = s_auth_username_len;
- hio_readbytes(io, 2);
- }
- }
- break;
- case s_auth_username_len:
- // printf("s_auth_username_len\n");
- {
- assert(readbytes == 2);
- uint8_t auth_version = bytes[0];
- uint8_t username_len = bytes[1];
- if (auth_version != SOCKS5_AUTH_VERSION || username_len == 0) {
- fprintf(stderr, "Unsupported auth version: %d\n", (int)auth_version);
- hio_close(io);
- return;
- }
- conn->state = s_auth_username;
- hio_readbytes(io, username_len);
- }
- break;
- case s_auth_username:
- // printf("s_auth_username\n");
- {
- char* username = (char*)bytes;
- printf("username=%.*s\n", readbytes, username);
- if (readbytes != strlen(auth_username) ||
- strncmp(username, auth_username, readbytes) != 0) {
- fprintf(stderr, "User authentication failed!\n");
- uint8_t resp[2] = { SOCKS5_AUTH_VERSION, SOCKS5_AUTH_FAILURE };
- hio_write(io, resp, 2);
- hio_close(io);
- return;
- }
- conn->state = s_auth_password_len;
- hio_readbytes(io, 1);
- }
- break;
- case s_auth_password_len:
- // printf("s_auth_password_len\n");
- {
- assert(readbytes == 1);
- uint8_t password_len = bytes[0];
- if (password_len == 0) {
- fprintf(stderr, "Miss password\n");
- uint8_t resp[2] = { SOCKS5_AUTH_VERSION, SOCKS5_AUTH_FAILURE };
- hio_write(io, resp, 2);
- hio_close(io);
- return;
- }
- conn->state = s_auth_password;
- hio_readbytes(io, password_len);
- }
- break;
- case s_auth_password:
- // printf("s_auth_password\n");
- {
- char* password = (char*)bytes;
- printf("password=%.*s\n", readbytes, password);
- uint8_t resp[2] = { SOCKS5_AUTH_VERSION, SOCKS5_AUTH_SUCCESS };
- if (readbytes != strlen(auth_password) ||
- strncmp(password, auth_password, readbytes) != 0) {
- fprintf(stderr, "User authentication failed!\n");
- resp[1] = SOCKS5_AUTH_FAILURE;
- hio_write(io, resp, 2);
- hio_close(io);
- return;
- }
- hio_write(io, resp, 2);
- conn->state = s_request;
- hio_readbytes(io, 3);
- }
- break;
- case s_request:
- // printf("s_request\n");
- {
- assert(readbytes == 3);
- uint8_t version = bytes[0];
- uint8_t cmd = bytes[1];
- if (version != SOCKS5_VERSION || cmd != ConnectCommand) {
- // TODO: Implement other commands
- fprintf(stderr, "Unsupported command: %d\n", (int)cmd);
- hio_close(io);
- return;
- }
- conn->state = s_dst_addr_type;
- hio_readbytes(io, 1);
- }
- break;
- case s_dst_addr_type:
- // printf("s_dst_addr_type\n");
- {
- assert(readbytes == 1);
- conn->addr_type = (socks5_addr_type)bytes[0];
- if (conn->addr_type == IPv4Addr) {
- conn->state = s_dst_addr;
- hio_readbytes(io, 4);
- } else if (conn->addr_type == FqdnAddr) {
- conn->state = s_dst_addr_len;
- hio_readbytes(io, 1);
- } else if (conn->addr_type == IPv6Addr) {
- conn->state = s_dst_addr;
- hio_readbytes(io, 16);
- } else {
- fprintf(stderr, "Unsupported addr type: %d\n", (int)conn->addr_type);
- hio_close(io);
- return;
- }
- }
- break;
- case s_dst_addr_len:
- // printf("s_dst_addr_len\n");
- {
- uint8_t addr_len = bytes[0];
- if (addr_len == 0) {
- fprintf(stderr, "Miss domain!\n");
- hio_close(io);
- return;
- }
- conn->state = s_dst_addr;
- hio_readbytes(io, addr_len);
- }
- break;
- case s_dst_addr:
- // printf("s_dst_addr\n");
- {
- if (conn->addr_type == IPv4Addr) {
- assert(readbytes == 4);
- conn->addr.sa.sa_family = AF_INET;
- memcpy(&conn->addr.sin.sin_addr, bytes, 4);
- } else if (conn->addr_type == IPv6Addr) {
- assert(readbytes == 16);
- conn->addr.sa.sa_family = AF_INET6;
- memcpy(&conn->addr.sin6.sin6_addr, bytes, 16);
- } else {
- char* host = NULL;
- STACK_OR_HEAP_ALLOC(host, readbytes + 1, 256);
- memcpy(host, bytes, readbytes);
- host[readbytes] = '\0';
- // TODO: async DNS
- int ret = ResolveAddr(host, &conn->addr);
- STACK_OR_HEAP_FREE(host);
- if (ret != 0) {
- fprintf(stderr, "Resolve %.*s failed!\n", readbytes, (char*)bytes);
- hio_close(io);
- return;
- }
- }
- conn->state = s_dst_port;
- hio_readbytes(io, 2);
- }
- break;
- case s_dst_port:
- // printf("s_dst_port\n");
- {
- assert(readbytes == 2);
- uint16_t port = ((uint16_t)bytes[0]) << 8 | bytes[1];
- // printf("port=%d\n", port);
- sockaddr_set_port(&conn->addr, port);
- hloop_t* loop = hevent_loop(io);
- // hio_t* upstream_io = hio_setup_tcp_upstream(io, conn->host, conn->port, 0);
- // hio_t* upstream_io = hio_create_socket(loop, conn->host, conn->port, HIO_TYPE_TCP, HIO_CLIENT_SIDE);
- int sockfd = socket(conn->addr.sa.sa_family, SOCK_STREAM, 0);
- if (sockfd < 0) {
- perror("socket");
- hio_close(io);
- return;
- }
- hio_t* upstream_io = hio_get(loop, sockfd);
- assert(upstream_io != NULL);
- hio_set_peeraddr(upstream_io, &conn->addr.sa, sockaddr_len(&conn->addr));
- hevent_set_userdata(upstream_io, conn);
- // io <=> upstream_io
- hio_setup_upstream(io, upstream_io);
- hio_setcb_connect(upstream_io, on_upstream_connect);
- hio_setcb_close(upstream_io, hio_close_upstream);
- conn->state = s_upstream;
- // printf("connect to ");
- // SOCKADDR_PRINT(hio_peeraddr(upstream_io));
- hio_connect(upstream_io);
- }
- break;
- case s_upstream:
- hio_write_upstream(io, buf, readbytes);
- break;
- case s_end:
- break;
- default:
- break;
- }
- }
- static void on_accept(hio_t* io) {
- /*
- printf("on_accept connfd=%d\n", hio_fd(io));
- char localaddrstr[SOCKADDR_STRLEN] = {0};
- char peeraddrstr[SOCKADDR_STRLEN] = {0};
- printf("accept connfd=%d [%s] <= [%s]\n", hio_fd(io),
- SOCKADDR_STR(hio_localaddr(io), localaddrstr),
- SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
- */
- hio_setcb_read(io, on_recv);
- hio_setcb_close(io, on_close);
- socks5_conn_t* conn = NULL;
- HV_ALLOC_SIZEOF(conn);
- conn->io = io;
- hevent_set_userdata(io, conn);
- // start read
- conn->state = s_auth_methods_count;
- hio_readbytes(io, 2);
- }
- int main(int argc, char** argv) {
- if (argc < 2) {
- printf("Usage: %s proxy_port [username] [password]\n", argv[0]);
- return -10;
- }
- proxy_port = atoi(argv[1]);
- if (argc > 3) {
- auth_username = argv[2];
- auth_password = argv[3];
- }
- hloop_t* loop = hloop_new(0);
- hio_t* listenio = hloop_create_tcp_server(loop, proxy_host, proxy_port, on_accept);
- if (listenio == NULL) {
- return -20;
- }
- printf("socks5 proxy server listening on %s:%d, listenfd=%d\n", proxy_host, proxy_port, hio_fd(listenio));
- hloop_run(loop);
- hloop_free(&loop);
- return 0;
- }
|