├── .editorconfig ├── .gitignore ├── .gitmodules ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── src ├── callback.c ├── callback.h ├── help.c ├── help.h ├── main.c ├── netutils.c ├── netutils.h ├── optparser.c ├── optparser.h ├── resolve.c ├── resolve.h ├── socks5.c └── socks5.h └── test └── test.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [Makefile] 13 | indent_style = tab 14 | tab_width = 4 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | .vscode 54 | debug 55 | ssserver 56 | !deps/udns-0.4/configure.lib 57 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/buffer"] 2 | path = src/buffer 3 | url = https://github.com/shiffthq/buffer.git 4 | [submodule "src/libev"] 5 | path = src/libev 6 | url = https://github.com/shadowsocks/libev.git 7 | [submodule "src/logger"] 8 | path = src/logger 9 | url = https://github.com/shiffthq/logger.git 10 | [submodule "src/udns"] 11 | path = src/udns 12 | url = https://github.com/ortclib/udns.git 13 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:latest 2 | 3 | RUN apt-get update 4 | RUN apt-get install build-essential -y 5 | RUN apt-get install gdb -y 6 | RUN apt-get install procps -y 7 | 8 | RUN apt-get install tzdata 9 | ENV TZ=Asia/Shanghai 10 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 11 | RUN dpkg-reconfigure -f noninteractive tzdata 12 | 13 | WORKDIR /app 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC := gcc 2 | 3 | CFLAGS := -g -Wall -O3 -DLINUX 4 | CXXFLAGS := $(CFLAGS) 5 | 6 | LDFLAGS := -Wl,-rpath,bin,-rpath, -lm -L./ \ 7 | -Isrc/libev -lev \ 8 | -Isrc/udns -ludns \ 9 | -Isrc/logger -llogger \ 10 | -Isrc/buffer -lbuffer 11 | 12 | vpath %.c src 13 | 14 | SOURCES := main.c netutils.c callback.c socks5.c resolve.c optparser.c help.c 15 | 16 | ssserver: $(SOURCES) libev.a libudns.a liblogger.a libbuffer.a 17 | $(CC) $^ $(CFLAGS) $(LDFLAGS) -g -o $@ 18 | 19 | libev.a: 20 | cd src/libev && ./configure && make 21 | cp src/libev/.libs/libev.a ./ 22 | 23 | libudns.a: 24 | cd src/udns && ./configure && make 25 | cp src/udns/libudns.a ./ 26 | 27 | liblogger.a: 28 | cd src/logger && make liblogger.a 29 | cp src/logger/liblogger.a ./ 30 | 31 | libbuffer.a: 32 | cd src/buffer && make libbuffer.a 33 | cp src/buffer/libbuffer.a ./ 34 | 35 | .PHONY: test 36 | test: 37 | @./test/test.sh 38 | 39 | clean: 40 | rm -rf ssserver 41 | rm -rf *.a 42 | rm -rf *.so 43 | 44 | builddebian: 45 | docker build -t debian:gcc . 46 | 47 | rundebian: 48 | docker run --name ss -p 23456:23456 -v ${PWD}:/app/ -it --rm debian:gcc bash 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## socks5 server 2 | 3 | ## Quick Start 4 | ``` 5 | $ git submodule update --init --recursive 6 | $ make 7 | $ ./ssserver 8 | ``` 9 | 10 | ## Help 11 | Call ssserver with the --help option for a list of command-line arguments. 12 | 13 | ## Testing 14 | Tests can be run with: 15 | ``` 16 | $ make test 17 | ``` 18 | The proxy server is expected to be bound to port 23456 for the tests to work. Start it with -p 23456. 19 | 20 | ### RFCs 21 | - [RFC1035: DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION](https://tools.ietf.org/html/rfc1035) 22 | - [RFC1928: SOCKS Protocol Version 5](https://tools.ietf.org/html/rfc1928) 23 | - [RFC1929: Username/Password Authentication for SOCKS V5](https://tools.ietf.org/html/rfc1929) 24 | -------------------------------------------------------------------------------- /src/callback.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "callback.h" 9 | #include "logger.h" 10 | #include "netutils.h" 11 | #include "resolve.h" 12 | #include "socks5.h" 13 | 14 | void accept_cb(struct ev_loop *loop, struct ev_io *w, int revents) { 15 | int fd = w->fd; 16 | struct socks5_server *server = (struct socks5_server *)w->data; 17 | 18 | while (1) { 19 | struct socks5_conn *conn = NULL; 20 | struct sockaddr_storage storage; 21 | socklen_t len = sizeof(struct sockaddr_storage); 22 | int clientfd; 23 | 24 | clientfd = accept(fd, (struct sockaddr *)&storage, &len); 25 | if (clientfd == -1) { 26 | if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) { 27 | logger_error("accept error: [%d]\n", errno); 28 | } 29 | break; 30 | } 31 | 32 | if (set_nonblocking(clientfd) < 0) { 33 | logger_error("set_nonblocking: [%d]\n", errno); 34 | goto _close_conn; 35 | } 36 | 37 | if (set_nosigpipe(clientfd) < 0) { 38 | logger_error("set_nosigpipe: [%d]\n", errno); 39 | goto _close_conn; 40 | } 41 | 42 | conn = socks5_conn_new(); 43 | if (NULL == conn) { 44 | logger_error("socks5_conn_new fail: [%d]\n", errno); 45 | goto _close_conn; 46 | } 47 | 48 | conn->loop = loop; 49 | conn->server = server; 50 | conn->client.fd = clientfd; 51 | ev_io_init(conn->client.rw, client_recv_cb, clientfd, EV_READ); 52 | ev_io_init(conn->client.ww, client_send_cb, clientfd, EV_WRITE); 53 | // start receive handleshake 54 | ev_io_start(loop, conn->client.rw); 55 | 56 | if (AF_INET == storage.ss_family) { 57 | char ip[INET_ADDRSTRLEN]; 58 | struct sockaddr_in *addr = (struct sockaddr_in *)&storage; 59 | logger_info("accept connection (host=%s, port=%d)\n", 60 | inet_ntop(storage.ss_family, &addr->sin_addr.s_addr, ip, sizeof(ip)), 61 | ntohs(addr->sin_port)); 62 | } else if (AF_INET6 == storage.ss_family) { 63 | char ip[INET6_ADDRSTRLEN]; 64 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; 65 | logger_info("accept connection (host=%s, port=%d)\n", 66 | inet_ntop(storage.ss_family, &addr->sin6_addr, ip, sizeof(ip)), 67 | ntohs(addr->sin6_port)); 68 | } 69 | 70 | continue; 71 | 72 | _close_conn: 73 | if (NULL != conn) { 74 | socks5_conn_close(conn); 75 | } else if (clientfd) { 76 | close(clientfd); 77 | } 78 | } 79 | } 80 | 81 | int connect_to_remote(struct socks5_conn *conn, struct sockaddr_storage *storage) { 82 | struct socks5_remote_conn *remote = &conn->remote; 83 | 84 | int fd = 0; 85 | socklen_t address_len; 86 | // use INET6_ADDRSTRLEN instead of INET_ADDRSTRLEN 87 | char ipaddr[INET6_ADDRSTRLEN]; 88 | 89 | fd = create_socket(storage->ss_family); 90 | if (fd < 0) { 91 | logger_debug("create_socket fail, errno: [%d]\n", errno); 92 | goto _err; 93 | } 94 | 95 | if (AF_INET == storage->ss_family) { 96 | struct sockaddr_in *addr = (struct sockaddr_in *)storage; 97 | address_len = sizeof(struct sockaddr_in); 98 | if (NULL == inet_ntop(storage->ss_family, &(addr->sin_addr), ipaddr, sizeof(ipaddr))) { 99 | logger_error("inet_ntop fail, errno: [%d]\n", errno); 100 | goto _err; 101 | } 102 | addr->sin_port = htons(remote->port); 103 | } else if (AF_INET6 == storage->ss_family) { 104 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; 105 | address_len = sizeof(struct sockaddr_in6); 106 | if (NULL == inet_ntop(storage->ss_family, &(addr->sin6_addr), ipaddr, sizeof(ipaddr))) { 107 | logger_error("inet_ntop fail, errno: [%d]\n", errno); 108 | goto _err; 109 | } 110 | addr->sin6_port = htons(remote->port); 111 | } else { 112 | logger_warn("invalid sa_family: [%d]\n", storage->ss_family); 113 | goto _err; 114 | } 115 | 116 | if ('\0' == remote->hostname[0]) { 117 | memcpy(remote->hostname, ipaddr, sizeof(ipaddr)); 118 | logger_info("connecting to remote host=%s, port=%d\n", remote->hostname, remote->port); 119 | } else { 120 | logger_info("connecting to remote host=%s(%s), port=%d\n", remote->hostname, ipaddr, remote->port); 121 | } 122 | 123 | if (connect(fd, (struct sockaddr *)storage, address_len) < 0) { 124 | if (EINPROGRESS != errno) { 125 | logger_debug("connect fail, errno: [%d]\n", errno); 126 | goto _err; 127 | } 128 | } 129 | 130 | return fd; 131 | _err: 132 | if (fd > 0) { 133 | close(fd); 134 | } 135 | 136 | return -1; 137 | } 138 | 139 | void dns_resolve_cb(struct sockaddr_storage *storage, struct resolve_query_t *query) { 140 | struct socks5_conn *conn = (struct socks5_conn *)query->data; 141 | struct ev_loop *loop = conn->loop; 142 | struct socks5_remote_conn *remote = &conn->remote; 143 | struct socks5_client_conn *client = &conn->client; 144 | 145 | struct socks5_response reply = { 146 | SOCKS5_VERSION, 147 | SOCKS5_RESPONSE_SERVER_FAILURE, 148 | SOCKS5_RSV, 149 | SOCKS5_ADDRTYPE_IPV4 150 | }; 151 | 152 | if (NULL == storage) { 153 | goto _err; 154 | } 155 | 156 | int remotefd = connect_to_remote(conn, storage); 157 | if (remotefd < 0) { 158 | logger_error("connect_to_remote fail, errno [%d]\n", errno); 159 | goto _err; 160 | } 161 | 162 | remote->fd = remotefd; 163 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_CONNECTING); 164 | ev_io_init(remote->rw, remote_recv_cb, remote->fd, EV_READ); 165 | ev_io_init(remote->ww, remote_send_cb, remote->fd, EV_WRITE); 166 | ev_io_start(loop, remote->ww); 167 | return; 168 | 169 | _err: 170 | buffer_concat(client->output, (char *)&reply, sizeof(reply)); 171 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_CLOSING); 172 | ev_io_start(loop, client->ww); 173 | } 174 | 175 | void client_recv_cb(struct ev_loop *loop, struct ev_io *w, int revents) { 176 | struct socks5_conn *conn = (struct socks5_conn *)w->data; 177 | struct socks5_server *server = conn->server; 178 | struct socks5_client_conn *client = &conn->client; 179 | struct socks5_remote_conn *remote = &conn->remote; 180 | int fd = w->fd; 181 | 182 | logger_debug("client_recv_cb start, fd: [%d], stage: [%d]\n", fd, conn->stage); 183 | 184 | char buffer[512]; 185 | ssize_t size; 186 | while (1) { 187 | size = read(fd, buffer, sizeof(buffer)); 188 | if (size < 0) { 189 | if (EAGAIN != errno && EWOULDBLOCK != errno) { 190 | logger_info("close connection [%d], errno: [%d]\n", fd, errno); 191 | goto _close_conn; 192 | } 193 | break; 194 | } else if (0 == size) { 195 | logger_debug("closed connection [%d]\n", fd); 196 | goto _close_conn; 197 | } else { 198 | buffer_concat(conn->client.input, buffer, size); 199 | } 200 | } 201 | 202 | logger_debug("client_recv_cb end, fd: [%d], stage: [%d]\n", fd, conn->stage); 203 | 204 | switch (conn->stage) { 205 | case SOCKS5_CONN_STAGE_EXMETHOD: { 206 | struct socks5_method_req *method_req; 207 | method_req = (struct socks5_method_req *)client->input->data; 208 | // verify version 209 | if (SOCKS5_VERSION != method_req->ver) { 210 | logger_debug("invalid socks5 version: [%d]\n", method_req->ver); 211 | goto _close_conn; 212 | } 213 | if (buffer_len(client->input) < (method_req->nmethods + 2)) { 214 | logger_debug("need more data\n"); 215 | // wating more data 216 | return; 217 | } 218 | 219 | struct socks5_method_res reply = { 220 | SOCKS5_VERSION, 221 | SOCKS5_AUTH_NOACCEPTABLE 222 | }; 223 | 224 | int i; 225 | for (i = 0; i < method_req->nmethods; i++) { 226 | logger_debug("auth methods: [%d]\n", method_req->methods[i]); 227 | if (server->auth_method == method_req->methods[i]) { 228 | reply.method = server->auth_method; 229 | conn->method = reply.method; 230 | } 231 | } 232 | 233 | logger_debug("auth method: [%d]\n", reply.method); 234 | 235 | buffer_concat(client->output, (char *)&reply, sizeof(reply)); 236 | if (SOCKS5_AUTH_NOACCEPTABLE == reply.method) { 237 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_CLOSING); 238 | } 239 | 240 | // reset recv buffer 241 | buffer_reset(client->input); 242 | ev_io_stop(loop, w); 243 | // send method response 244 | ev_io_start(loop, client->ww); 245 | break; 246 | } 247 | case SOCKS5_CONN_STAGE_USERNAMEPASSWORD: { 248 | struct socks5_userpass_req req; 249 | memset(&req, 0, sizeof(struct socks5_userpass_req)); 250 | 251 | req.ver = *client->input->data; 252 | if (SOCKS5_AUTH_USERNAMEPASSWORD_VER != req.ver) { 253 | logger_debug("invalid socks5 version: [%d]\n", *client->input->data); 254 | goto _close_conn; 255 | } 256 | 257 | if (buffer_len(client->input) < 2) { 258 | logger_warn("no username len, need more data\n"); 259 | // wating more data 260 | return; 261 | } 262 | req.ulen = *(client->input->data + 1); 263 | if (buffer_len(client->input) < (2 + req.ulen)) { 264 | logger_warn("no username, need more data\n"); 265 | // wating more data 266 | return; 267 | } 268 | memcpy(req.username, client->input->data + 2, req.ulen); 269 | 270 | if (buffer_len(client->input) < (req.ulen + 3)) { 271 | logger_warn("no password len, need more data\n"); 272 | // wating more data 273 | return; 274 | } 275 | req.plen = *(client->input->data + req.ulen + 2); 276 | if (buffer_len(client->input) < (req.ulen + req.plen + 3)) { 277 | logger_warn("no password, need more data\n"); 278 | // wating more data 279 | return; 280 | } 281 | memcpy(req.password, client->input->data + req.ulen + 3, req.plen); 282 | 283 | logger_debug("username/password: [%s]/[%s]\n", req.username, req.password); 284 | 285 | struct socks5_userpass_res res = { 286 | SOCKS5_AUTH_USERNAMEPASSWORD_VER, 287 | SOCKS5_AUTH_USERNAMEPASSWORD_STATUS_FAIL 288 | }; 289 | 290 | if (server->ulen == req.ulen && 291 | server->plen == req.plen && 292 | 0 == memcmp(&server->username, &req.username, req.ulen) && 293 | 0 == memcmp(&server->password, &req.password, req.ulen)) { 294 | res.status = SOCKS5_AUTH_USERNAMEPASSWORD_STATUS_OK; 295 | } 296 | 297 | if (SOCKS5_AUTH_USERNAMEPASSWORD_STATUS_FAIL == res.status) { 298 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_CONNECTING); 299 | } 300 | 301 | buffer_concat(client->output, (char *)&res, sizeof(struct socks5_userpass_res)); 302 | 303 | buffer_reset(client->input); 304 | ev_io_stop(loop, w); 305 | ev_io_start(loop, client->ww); 306 | break; 307 | } 308 | case SOCKS5_CONN_STAGE_EXHOST: { 309 | struct socks5_request *req = (struct socks5_request *)client->input->data; 310 | // verify version 311 | if (SOCKS5_VERSION != req->ver) { 312 | goto _close_conn; 313 | } 314 | 315 | // wait more data 316 | if (buffer_len(client->input) < sizeof(struct socks5_request)) { 317 | return; 318 | } 319 | 320 | struct socks5_response reply = { 321 | SOCKS5_VERSION, 322 | SOCKS5_RESPONSE_SUCCESS, 323 | SOCKS5_RSV, 324 | SOCKS5_ADDRTYPE_IPV4 325 | }; 326 | 327 | if (SOCKS5_CMD_CONNECT != req->cmd) { 328 | logger_warn("not supported cmd: [%d]\n", req->cmd); 329 | reply.rep = SOCKS5_RESPONSE_COMMAND_NOT_SUPPORTED; 330 | goto _response_fail; 331 | } 332 | 333 | remote->addrtype = req->addrtype; 334 | struct sockaddr_storage storage; 335 | memset(&storage, 0, sizeof(struct sockaddr_storage)); 336 | 337 | logger_debug("addrtype [%d]\n", req->addrtype); 338 | switch (req->addrtype) { 339 | case SOCKS5_ADDRTYPE_IPV4: { 340 | if (buffer_len(client->input) < (sizeof(struct socks5_request) + 6)) { 341 | logger_debug("wait more data\n"); 342 | return; 343 | } 344 | 345 | struct sockaddr_in *addr = (struct sockaddr_in *)&storage; 346 | addr->sin_family = AF_INET; 347 | 348 | char *host = client->input->data + sizeof(struct socks5_request); 349 | char *port = host + 4; 350 | memcpy(&addr->sin_addr.s_addr, host, 4); 351 | memcpy(&addr->sin_port, port, 2); 352 | remote->port = ntohs(addr->sin_port); 353 | 354 | buffer_concat(remote->bndaddr, (char *)&addr->sin_addr.s_addr, 4); 355 | buffer_concat(remote->bndaddr, (char *)&addr->sin_port, 2); 356 | break; 357 | } 358 | case SOCKS5_ADDRTYPE_DOMAIN: { 359 | // hostname length 360 | if (buffer_len(client->input) < (sizeof(struct socks5_request) + 1)) { 361 | logger_debug("wait more data\n"); 362 | return; 363 | } 364 | int hostname_len = *(client->input->data + sizeof(struct socks5_request)); 365 | if (buffer_len(client->input) < (sizeof(struct socks5_request) + hostname_len + 3)) { 366 | logger_debug("wait more data\n"); 367 | return; 368 | } 369 | 370 | memcpy(remote->hostname, client->input->data + sizeof(struct socks5_request) + 1, hostname_len); 371 | 372 | char *port = client->input->data + sizeof(struct socks5_request) + 1 + hostname_len; 373 | uint16_t sin_port; 374 | memcpy(&sin_port, port, 2); 375 | remote->port = ntohs(sin_port); 376 | 377 | logger_info("remote hostname: [%s:%d]\n", remote->hostname, remote->port); 378 | 379 | if (strtosockaddr(remote->hostname, (void *)&storage) > 0) { 380 | if (storage.ss_family == AF_INET) { 381 | remote->addrtype = SOCKS5_ADDRTYPE_IPV4; 382 | struct sockaddr_in *addr = (struct sockaddr_in *)&storage; 383 | addr->sin_port = htons(remote->port); 384 | 385 | buffer_concat(remote->bndaddr, (char *)&addr->sin_addr.s_addr, 4); 386 | buffer_concat(remote->bndaddr, (char *)&addr->sin_port, 2); 387 | } else if (storage.ss_family == AF_INET6) { 388 | remote->addrtype = SOCKS5_ADDRTYPE_IPV6; 389 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; 390 | addr->sin6_port = htons(remote->port); 391 | buffer_concat(remote->bndaddr, (char *)&addr->sin6_addr, 16); 392 | buffer_concat(remote->bndaddr, (char *)&addr->sin6_port, 2); 393 | } 394 | break; 395 | } else { 396 | buffer_concat(remote->bndaddr, client->input->data + sizeof(struct socks5_request), hostname_len + 3); 397 | remote->query = resolve_query(remote->hostname, dns_resolve_cb, conn); 398 | if (NULL == remote->query) { 399 | logger_error("resolve_query fail\n"); 400 | reply.rep = SOCKS5_RESPONSE_SERVER_FAILURE; 401 | goto _response_fail; 402 | } 403 | 404 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_DNSQUERY); 405 | buffer_reset(client->input); 406 | ev_io_stop(loop, w); 407 | return; 408 | } 409 | break; 410 | } 411 | case SOCKS5_ADDRTYPE_IPV6: { 412 | if (buffer_len(client->input) < (sizeof(struct socks5_request) + 18)) { 413 | logger_debug("wait more data\n"); 414 | return; 415 | } 416 | 417 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; 418 | addr->sin6_family = AF_INET6; 419 | 420 | char *host = client->input->data + sizeof(struct socks5_request); 421 | char *port = host + 16; 422 | memcpy(&addr->sin6_addr, host, 16); 423 | memcpy(&addr->sin6_port, port, 2); 424 | remote->port = ntohs(addr->sin6_port); 425 | 426 | buffer_concat(remote->bndaddr, (char *)&addr->sin6_addr, 16); 427 | buffer_concat(remote->bndaddr, (char *)&addr->sin6_port, 2); 428 | break; 429 | } 430 | default: 431 | logger_warn("not supported addrtype: [%d]\n", req->addrtype); 432 | reply.rep = SOCKS5_RESPONSE_ADDRTYPE_NOT_SUPPORTED; 433 | goto _response_fail; 434 | } 435 | 436 | ev_io_stop(loop, w); 437 | buffer_reset(client->input); 438 | 439 | int remotefd = connect_to_remote(conn, &storage); 440 | if (remotefd < 0) { 441 | logger_error("connect_to_remote fail, errno [%d]\n", errno); 442 | goto _response_fail; 443 | } 444 | 445 | remote->fd = remotefd; 446 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_CONNECTING); 447 | ev_io_init(remote->rw, remote_recv_cb, remote->fd, EV_READ); 448 | ev_io_init(remote->ww, remote_send_cb, remote->fd, EV_WRITE); 449 | ev_io_start(loop, remote->ww); 450 | break; 451 | _response_fail: 452 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_CLOSING); 453 | reply.rep = SOCKS5_RESPONSE_SERVER_FAILURE; 454 | buffer_concat(conn->client.output, (char *)&reply, sizeof(reply)); 455 | ev_io_start(loop, client->ww); 456 | break; 457 | } 458 | case SOCKS5_CONN_STAGE_STREAM: 459 | // send to remote 460 | buffer_concat(remote->output, (char *)client->input->data, buffer_len(client->input)); 461 | buffer_reset(client->input); 462 | ev_io_start(loop, remote->ww); 463 | break; 464 | default: 465 | logger_warn("unexpect stage [%d]\n", conn->stage); 466 | goto _close_conn; 467 | } 468 | 469 | logger_debug("client_recv_cb handled, fd: [%d], stage: [%d]\n", fd, conn->stage); 470 | 471 | // continue 472 | return; 473 | _close_conn: 474 | logger_debug("client_recv_cb close conn, fd: [%d], stage: [%d]\n", fd, conn->stage); 475 | socks5_conn_close(conn); 476 | } 477 | 478 | void client_send_cb(struct ev_loop *loop, struct ev_io *w, int revents) { 479 | struct socks5_conn *conn = (struct socks5_conn *)w->data; 480 | struct socks5_client_conn *client = &conn->client; 481 | struct socks5_remote_conn *remote = &conn->remote; 482 | 483 | int fd = w->fd; 484 | 485 | logger_debug("client_send_cb start, fd: [%d], stage: [%d]\n", fd, conn->stage); 486 | 487 | ssize_t idx = 0; 488 | ssize_t size; 489 | 490 | while (1) { 491 | size_t remain = buffer_len(client->output) - idx; 492 | if (remain <= 0) { 493 | // all data send, stop 494 | buffer_reset(client->output); 495 | ev_io_stop(loop, w); 496 | break; 497 | } 498 | 499 | size = write(fd, client->output->data + idx, remain); 500 | if (size < 0) { 501 | if (EAGAIN != errno && EWOULDBLOCK != errno) { 502 | logger_debug("write fail, fd [%d], errno: [%d]\n", fd, errno); 503 | goto _close_conn; 504 | } 505 | // send buffer full, wait new event 506 | logger_warn("client_send_cb output buffer full, fd: [%d]\n", fd); 507 | buffer_t *buf = buffer_new(remain); 508 | if (NULL == buf) { 509 | logger_error("buffer_new fail, errno: [%d]\n", errno); 510 | goto _close_conn; 511 | } 512 | buffer_concat(buf, client->output->data + idx, remain); 513 | buffer_free(client->output); 514 | client->output = buf; 515 | break; 516 | } else { 517 | idx += size; 518 | } 519 | } 520 | 521 | if (SOCKS5_CONN_STAGE_EXMETHOD == conn->stage) { 522 | // change stage after exchange method 523 | if (SOCKS5_AUTH_NOAUTH == conn->method) { 524 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_EXHOST); 525 | } else if (SOCKS5_AUTH_USERNAMEPASSWORD == conn->method) { 526 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_USERNAMEPASSWORD); 527 | } 528 | // start receive new EXHOST/USERNAMEPASSWORD request 529 | ev_io_start(loop, client->rw); 530 | } else if (SOCKS5_CONN_STAGE_USERNAMEPASSWORD == conn->stage) { 531 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_EXHOST); 532 | // start receive EXHOST request 533 | ev_io_start(loop, client->rw); 534 | } else if (SOCKS5_CONN_STAGE_CONNECTED == conn->stage) { 535 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_STREAM); 536 | // start read real data 537 | ev_io_start(loop, client->rw); 538 | ev_io_start(loop, remote->rw); 539 | } 540 | 541 | logger_debug("client_send_cb end, fd: [%d], stage: [%d]\n", fd, conn->stage); 542 | 543 | // closing connection ? 544 | if (SOCKS5_CONN_STAGE_CLOSING == conn->stage) { 545 | goto _close_conn; 546 | } 547 | return; 548 | 549 | _close_conn: 550 | logger_debug("client_send_cb close conn, fd: [%d], stage: [%d]\n", fd, conn->stage); 551 | socks5_conn_close(conn); 552 | } 553 | 554 | void remote_recv_cb(struct ev_loop *loop, struct ev_io *w, int revents) { 555 | struct socks5_conn *conn = (struct socks5_conn *)w->data; 556 | struct socks5_client_conn *client = &conn->client; 557 | struct socks5_remote_conn *remote = &conn->remote; 558 | int fd = w->fd; 559 | 560 | logger_debug("remote_recv_cb start, fd: [%d], stage: [%d]\n", fd, conn->stage); 561 | 562 | ssize_t size; 563 | char buffer[512]; 564 | 565 | while (1) { 566 | size = read(fd, buffer, sizeof(buffer)); 567 | if (size < 0) { 568 | if (EAGAIN != errno && EWOULDBLOCK != errno) { 569 | logger_debug("close remote connection [%d], errno: [%d]\n", fd, errno); 570 | goto _close_conn; 571 | } 572 | break; 573 | } else if (0 == size) { 574 | // remote closed, send rest data to client and then close 575 | logger_debug("closed remote connection [%d]\n", fd); 576 | ev_io_stop(loop, w); 577 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_CLOSING); 578 | break; 579 | } else { 580 | buffer_concat(remote->input, buffer, size); 581 | } 582 | } 583 | 584 | buffer_concat(client->output, remote->input->data, buffer_len(remote->input)); 585 | buffer_reset(remote->input); 586 | ev_io_start(loop, client->ww); 587 | 588 | logger_debug("remote_recv_cb end, fd: [%d], stage: [%d]\n", fd, conn->stage); 589 | 590 | return; 591 | 592 | _close_conn: 593 | logger_debug("remote_recv_cb close conn, fd: [%d], stage: [%d]\n", fd, conn->stage); 594 | socks5_conn_close(conn); 595 | } 596 | 597 | void remote_send_cb(struct ev_loop *loop, struct ev_io *w, int revents) { 598 | struct socks5_conn *conn = (struct socks5_conn *)w->data; 599 | struct socks5_remote_conn *remote = &conn->remote; 600 | struct socks5_client_conn *client = &conn->client; 601 | int fd = w->fd; 602 | 603 | // connect remote success 604 | if (SOCKS5_CONN_STAGE_CONNECTING == conn->stage) { 605 | logger_debug("remote connected, fd: [%d], stage: [%d]\n", fd, conn->stage); 606 | 607 | struct socks5_response reply; 608 | reply.ver = SOCKS5_VERSION; 609 | reply.rep = SOCKS5_RESPONSE_SUCCESS; 610 | reply.addrtype = remote->addrtype; 611 | int remotefd = fd; 612 | 613 | struct sockaddr_storage storage; 614 | socklen_t len = sizeof(storage); 615 | if (getpeername(remotefd, (struct sockaddr *)&storage, &len) < 0) { 616 | logger_warn("getpeername(%s:%d) fail, errno: [%d]\n", remote->hostname, remote->port, errno); 617 | // something wrong 618 | reply.rep = SOCKS5_RESPONSE_SERVER_FAILURE; 619 | buffer_concat(client->output, (char *)&reply, sizeof(reply)); 620 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_CLOSING); 621 | } else { 622 | // connected 623 | socks5_conn_setstage(conn, SOCKS5_CONN_STAGE_CONNECTED); 624 | buffer_concat(client->output, (char *)&reply, sizeof(reply)); 625 | buffer_concat(client->output, remote->bndaddr->data, buffer_len(remote->bndaddr)); 626 | logger_info("remote connected host=%s, port=%d\n", remote->hostname, remote->port); 627 | } 628 | 629 | // notify client connect result 630 | ev_io_start(loop, client->ww); 631 | ev_io_stop(loop, w); 632 | 633 | return; 634 | } 635 | 636 | logger_debug("remote_send_cb start, fd: [%d], stage: [%d]\n", fd, conn->stage); 637 | 638 | ssize_t idx = 0; 639 | ssize_t size; 640 | while (1) { 641 | size_t remain = buffer_len(remote->output) - idx; 642 | if (remain <= 0) { 643 | // all data send, stop 644 | buffer_reset(remote->output); 645 | ev_io_stop(loop, w); 646 | break; 647 | } 648 | 649 | size = write(fd, remote->output->data + idx, remain); 650 | if (size < 0) { 651 | if (EAGAIN != errno && EWOULDBLOCK != errno) { 652 | logger_debug("write fail, fd [%d], errno: [%d]\n", fd, errno); 653 | goto _close_conn; 654 | } 655 | // send buffer full, wait new event 656 | logger_warn("remote_send_cb output buffer full, fd: [fd]\n", fd); 657 | buffer_t *buf = buffer_new(remain); 658 | if (NULL == buf) { 659 | logger_error("buffer_new fail, errno: [%d]\n", errno); 660 | goto _close_conn; 661 | } 662 | buffer_concat(buf, remote->output->data + idx, remain); 663 | buffer_free(remote->output); 664 | remote->output = buf; 665 | break; 666 | } else { 667 | idx += size; 668 | } 669 | } 670 | 671 | logger_debug("remote_send_cb end, fd: [%d], stage: [%d]\n", fd, conn->stage); 672 | 673 | return; 674 | _close_conn: 675 | logger_debug("remote_send_cb close conn, fd: [%d], stage: [%d]\n", fd, conn->stage); 676 | socks5_conn_close(conn); 677 | } 678 | -------------------------------------------------------------------------------- /src/callback.h: -------------------------------------------------------------------------------- 1 | #ifndef CALLBACK_H 2 | #define CALLBACK_H 3 | 4 | #include 5 | 6 | void accept_cb(struct ev_loop *loop, struct ev_io *w, int revents); 7 | 8 | void client_recv_cb(struct ev_loop *loop, struct ev_io *w, int revents); 9 | void client_send_cb(struct ev_loop *loop, struct ev_io *w, int revents); 10 | void remote_recv_cb(struct ev_loop *loop, struct ev_io *w, int revents); 11 | void remote_send_cb(struct ev_loop *loop, struct ev_io *w, int revents); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/help.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "help.h" 3 | 4 | void help() { 5 | printf("ssserver\n"); 6 | 7 | printf(" --username username for auth\n"); 8 | printf(" --password password for auth\n"); 9 | printf(" -p, --port server port, default to 1080\n"); 10 | printf(" -d run in daemon\n"); 11 | printf(" --loglevel log levels: fatal, error, warning, info, debug, trace\n"); 12 | printf(" -h, --help help\n"); 13 | } 14 | -------------------------------------------------------------------------------- /src/help.h: -------------------------------------------------------------------------------- 1 | #ifndef __HELP_H 2 | #define __HELP_H 3 | 4 | void help(); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "logger.h" 14 | #include "netutils.h" 15 | #include "resolve.h" 16 | #include "callback.h" 17 | #include "socks5.h" 18 | #include "optparser.h" 19 | #include "help.h" 20 | 21 | #define EPOLL_SIZE 1024 22 | #define LISTEN_BACKLOG 128 23 | #define MAX_EVENTS_COUNT 128 24 | #define LISTEN_PORT 1080 25 | 26 | static void signal_handler(int sig) { 27 | logger_info("receive signal: [%d]\n", sig); 28 | 29 | switch (sig) { 30 | case SIGINT: 31 | case SIGTERM: 32 | break; 33 | default: 34 | logger_warn("unknown signal [%d]\n", sig); 35 | } 36 | } 37 | 38 | static int register_signals() { 39 | // ctrl + c 40 | if (SIG_ERR == signal(SIGINT, signal_handler)) { 41 | logger_error("register signal SIGINT fail\n"); 42 | return -1; 43 | } 44 | 45 | // pkill 46 | if (SIG_ERR == signal(SIGTERM, signal_handler)) { 47 | logger_error("register signal SIGTERM fail\n"); 48 | return -1; 49 | } 50 | 51 | return 0; 52 | } 53 | 54 | int create_and_bind(uint16_t port, int32_t backlog) { 55 | int sockfd = socket(AF_INET6, SOCK_STREAM, 0); 56 | if (sockfd < 0) { 57 | logger_error("sockfd create failed"); 58 | return -1; 59 | } 60 | 61 | struct sockaddr_in6 servaddr; 62 | memset((char *)&servaddr, 0, sizeof(servaddr)); 63 | servaddr.sin6_family = AF_INET6; 64 | servaddr.sin6_addr = in6addr_any; 65 | servaddr.sin6_port = htons(port); 66 | 67 | if (set_nonblocking(sockfd) < 0) { 68 | logger_error("set_nonblocking fail [%d]\n", errno); 69 | close(sockfd); 70 | return -1; 71 | } 72 | 73 | if (set_reuseaddr(sockfd) < 0) { 74 | logger_error("set_reuseaddr fail [%d]\n", errno); 75 | close(sockfd); 76 | return -1; 77 | } 78 | 79 | if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { 80 | logger_error("bind error [%d]\n", errno); 81 | close(sockfd); 82 | return -1; 83 | } 84 | 85 | if (listen(sockfd, backlog) < 0) { 86 | logger_error("listen error [%d]!\n", errno); 87 | close(sockfd); 88 | return -1; 89 | } 90 | 91 | return sockfd; 92 | } 93 | 94 | struct socks5_server g_server = { 95 | 0, 96 | "", 97 | 0, 98 | "", 99 | LISTEN_PORT, 100 | SOCKS5_AUTH_NOAUTH, 101 | false, 102 | LOGGER_LEVEL_INFO 103 | }; 104 | 105 | int main (int argc, char **argv) { 106 | if (socks5_server_parse(argc, argv) < 0) { 107 | help(); 108 | exit(EXIT_FAILURE); 109 | } 110 | 111 | logger_init(NULL, g_server.log_level | LOGGER_COLOR_ON); 112 | 113 | if (g_server.daemon) { 114 | if (daemon(1, 0) < 0) { 115 | logger_error("daemon fail, errno [%d]\n", errno); 116 | exit(EXIT_FAILURE); 117 | } 118 | } 119 | 120 | logger_info("starting ...\n"); 121 | 122 | struct ev_loop *loop = ev_default_loop(0); 123 | struct ev_io server_watcher; 124 | 125 | if (resolve_init(loop, NULL, 0) < 0) { 126 | logger_error("resolve_init fail\n"); 127 | exit(EXIT_FAILURE); 128 | } else { 129 | logger_info("resolve_init ok\n"); 130 | } 131 | 132 | // if (register_signals() < 0) { 133 | // logger_error("register_signals fail, errno: [%d]\n", errno); 134 | // exit(EXIT_FAILURE); 135 | // } 136 | 137 | server_watcher.fd = create_and_bind(g_server.port, LISTEN_BACKLOG); 138 | server_watcher.data = &g_server; 139 | if (server_watcher.fd < 0) { 140 | logger_error("create_and_bind fail, errno: [%d]\n", errno); 141 | exit(EXIT_FAILURE); 142 | } 143 | 144 | ev_io_init(&server_watcher, accept_cb, server_watcher.fd, EV_READ); 145 | ev_io_start(loop, &server_watcher); 146 | 147 | logger_info("start working, port: [%d], auth_method: [%d]\n", g_server.port, g_server.auth_method); 148 | ev_run(loop, 0); 149 | 150 | logger_info("exiting ...\n"); 151 | 152 | resolve_shutdown(loop); 153 | ev_loop_destroy(loop); 154 | 155 | return EXIT_SUCCESS; 156 | } 157 | -------------------------------------------------------------------------------- /src/netutils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "netutils.h" 9 | 10 | int create_socket(int af) { 11 | int fd = socket(af, SOCK_STREAM, 0); 12 | 13 | if (fd < 0) { 14 | return -1; 15 | } 16 | 17 | if (set_nonblocking(fd) < 0) { 18 | close(fd); 19 | return -1; 20 | } 21 | 22 | if (set_reuseaddr(fd) < 0) { 23 | close(fd); 24 | return -1; 25 | } 26 | 27 | if (set_nosigpipe(fd) < 0) { 28 | close(fd); 29 | return -1; 30 | } 31 | 32 | return fd; 33 | } 34 | 35 | int set_nonblocking(int fd) { 36 | int flags; 37 | 38 | flags = fcntl(fd, F_GETFL, 0); 39 | if (flags == -1) { 40 | return -1; 41 | } 42 | 43 | return fcntl(fd, F_SETFL, flags | O_NONBLOCK); 44 | } 45 | 46 | int set_reuseaddr(int fd) { 47 | int opt = 1; 48 | return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt)); 49 | } 50 | 51 | int set_nodelay(int fd) { 52 | int opt = 1; 53 | return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&opt, sizeof(opt)); 54 | } 55 | 56 | int set_nosigpipe(int fd) { 57 | #ifdef SO_NOSIGPIPE 58 | int opt = 1; 59 | return setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(opt)); 60 | #endif 61 | return 0; 62 | } 63 | 64 | int strtosockaddr(const char *src, void *addrptr) { 65 | int ret; 66 | 67 | struct sockaddr_storage *storage = (struct sockaddr_storage *)addrptr; 68 | 69 | struct sockaddr_in addr4; 70 | ret = inet_pton(AF_INET, src, &(addr4.sin_addr)); 71 | if (ret > 0) { 72 | storage->ss_family = AF_INET; 73 | struct sockaddr_in *addr = (struct sockaddr_in *)addrptr; 74 | memcpy(&addr->sin_addr, &addr4.sin_addr, sizeof(addr4.sin_addr)); 75 | return ret; 76 | } 77 | 78 | struct sockaddr_in6 addr6; 79 | ret = inet_pton(AF_INET6, src, &(addr6.sin6_addr)); 80 | if (ret > 0) { 81 | storage->ss_family = AF_INET6; 82 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *)addrptr; 83 | memcpy(&addr->sin6_addr, &addr6.sin6_addr, sizeof(addr6.sin6_addr)); 84 | return ret; 85 | } 86 | 87 | return -1; 88 | } 89 | -------------------------------------------------------------------------------- /src/netutils.h: -------------------------------------------------------------------------------- 1 | #ifndef NETUTILS_H 2 | #define NETUTILS_H 3 | 4 | int create_socket(); 5 | int set_nonblocking(int fd); 6 | int set_reuseaddr(int fd); 7 | int set_nodelay(int fd); 8 | int set_nosigpipe(int fd); 9 | // int set_linger(int fd); 10 | 11 | int strtosockaddr(const char *src, void *addrptr); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/optparser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "optparser.h" 5 | #include "socks5.h" 6 | #include "logger.h" 7 | 8 | extern struct socks5_server g_server; 9 | 10 | int socks5_server_parse(int argc, char **argv) { 11 | 12 | #define OPTION_USERNAME_IDX 1 13 | #define OPTION_PASSWORD_IDX 2 14 | #define OPTION_LOGLEVEL_IDX 3 15 | 16 | int option_index = 0; 17 | struct option long_options[] = { 18 | { "username", required_argument, &option_index, OPTION_USERNAME_IDX }, 19 | { "password", required_argument, &option_index, OPTION_PASSWORD_IDX }, 20 | { "port", required_argument, NULL, 'p' }, 21 | { "daemon", required_argument, NULL, 'd' }, 22 | { "loglevel", required_argument, &option_index, OPTION_LOGLEVEL_IDX }, 23 | { "help", no_argument, NULL, 'h' }, 24 | { 0, 0, NULL, 0 } 25 | }; 26 | 27 | int c; 28 | 29 | while (1) { 30 | c = getopt_long (argc, argv, "hdp:", long_options, NULL); 31 | 32 | // end of the options. 33 | if (-1 == c) { 34 | break; 35 | } 36 | 37 | switch (c) { 38 | case 'd': 39 | logger_debug("run as daemon\n"); 40 | g_server.daemon = true; 41 | break; 42 | case 'p': 43 | g_server.port = atoi(optarg); 44 | logger_debug("port: [%d]\n", g_server.port); 45 | break; 46 | case 0: { 47 | switch (option_index) { 48 | case OPTION_USERNAME_IDX: { 49 | int ulen = strlen(optarg); 50 | int maxulen = SOCKS5_AUTH_USERNAMEPASSWORD_MAX_LEN; 51 | if (ulen >= maxulen) { 52 | logger_error("username exceed length [%d]\n", maxulen); 53 | return -1; 54 | } 55 | stpcpy(g_server.username, optarg); 56 | g_server.ulen = ulen; 57 | g_server.auth_method = SOCKS5_AUTH_USERNAMEPASSWORD; 58 | logger_debug("username: [%s]\n", g_server.username); 59 | break; 60 | } 61 | case OPTION_PASSWORD_IDX: { 62 | int plen = strlen(optarg); 63 | int maxplen = SOCKS5_AUTH_USERNAMEPASSWORD_MAX_LEN; 64 | if (plen >= maxplen) { 65 | logger_error("password exceed length [%d]\n", maxplen); 66 | return -1; 67 | } 68 | logger_debug("password: [%s]\n", g_server.password); 69 | g_server.plen = plen; 70 | stpcpy(g_server.password, optarg); 71 | break; 72 | } 73 | case OPTION_LOGLEVEL_IDX: { 74 | if (0 == strcmp("trace", optarg)) { 75 | g_server.log_level = LOGGER_LEVEL_TRACE; 76 | } else if (0 == strcmp("debug", optarg)) { 77 | g_server.log_level = LOGGER_LEVEL_DEBUG; 78 | } else if (0 == strcmp("info", optarg)) { 79 | g_server.log_level = LOGGER_LEVEL_INFO; 80 | } else if (0 == strcmp("warning", optarg)) { 81 | g_server.log_level = LOGGER_LEVEL_WARNING; 82 | } else if (0 == strcmp("error", optarg)) { 83 | g_server.log_level = LOGGER_LEVEL_ERROR; 84 | } else if (0 == strcmp("fatal", optarg)) { 85 | g_server.log_level = LOGGER_LEVEL_FATAL; 86 | } 87 | logger_debug("log level: [%s]\n", optarg); 88 | break; 89 | } 90 | default: 91 | return -1; 92 | } 93 | break; 94 | } 95 | default: 96 | return -1; 97 | } 98 | } 99 | 100 | return 0; 101 | } 102 | -------------------------------------------------------------------------------- /src/optparser.h: -------------------------------------------------------------------------------- 1 | #ifndef __OPTPARSER_H 2 | #define __OPTPARSER_H 3 | 4 | #include 5 | #include "socks5.h" 6 | 7 | int socks5_server_parse(int argc, char **argv); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/resolve.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "resolve.h" 7 | #include "netutils.h" 8 | #include "logger.h" 9 | 10 | static struct dns_ctx *g_ctx = &dns_defctx; 11 | static struct ev_io g_resolve_io_watcher; 12 | static struct ev_timer g_resolve_timeout_watcher; 13 | 14 | static void resolve_sock_cb(struct ev_loop *loop, struct ev_io *w, int revents) { 15 | struct dns_ctx *ctx = (struct dns_ctx *)w->data; 16 | 17 | logger_trace("resolve_sock_cb %x\n", revents); 18 | 19 | if (revents & EV_READ) { 20 | dns_ioevent(ctx, ev_now(loop)); 21 | } 22 | } 23 | 24 | static void resolve_timeout_cb(struct ev_loop *loop, struct ev_timer *w, int revents) { 25 | logger_trace("resolve_timeout_cb %x\n", revents); 26 | 27 | if (revents & EV_TIMER) { 28 | int next = dns_timeouts(NULL, -1, ev_now(loop)); 29 | 30 | if (next > 0) { 31 | w->repeat = next; 32 | ev_timer_again (EV_A_ w); 33 | } else { 34 | w->repeat = 1; 35 | ev_timer_again (EV_A_ w); 36 | } 37 | } 38 | } 39 | 40 | static void dns_timer_setup_cb(struct dns_ctx *ctx, int timeout, void *data) { 41 | struct ev_loop *loop = (struct ev_loop *)data; 42 | 43 | logger_trace("dns_timer_setup_cb %d\n", timeout); 44 | 45 | if (ev_is_active(&g_resolve_timeout_watcher)) { 46 | ev_timer_stop(loop, &g_resolve_timeout_watcher); 47 | } 48 | 49 | if (ctx != NULL && timeout >= 0) { 50 | ev_timer_set(&g_resolve_timeout_watcher, timeout, 0.0); 51 | ev_timer_start(loop, &g_resolve_timeout_watcher); 52 | } 53 | } 54 | 55 | int resolve_init(struct ev_loop *loop, char **nameservers, int nameserver_num) { 56 | int i; 57 | if (NULL == nameservers) { 58 | dns_reset(g_ctx); 59 | dns_init(g_ctx, 0); 60 | } else { 61 | dns_reset(g_ctx); 62 | 63 | for (i = 0; i < nameserver_num; i++) { 64 | dns_add_serv(g_ctx, nameservers[i]); 65 | } 66 | } 67 | 68 | int sockfd = dns_open(g_ctx); 69 | if (sockfd < 0) { 70 | logger_error("dns_open fail, errno: [%d]\n", errno); 71 | return -1; 72 | } 73 | 74 | if (set_nonblocking(sockfd) < 0) { 75 | logger_error("set_nonblocking, errno: [%d]\n", errno); 76 | close(sockfd); 77 | return -1; 78 | } 79 | 80 | ev_io_init(&g_resolve_io_watcher, resolve_sock_cb, sockfd, EV_READ); 81 | g_resolve_io_watcher.data = g_ctx; 82 | ev_io_start(loop, &g_resolve_io_watcher); 83 | 84 | ev_timer_init(&g_resolve_timeout_watcher, resolve_timeout_cb, 0, 0.0); 85 | g_resolve_timeout_watcher.data = g_ctx; 86 | ev_timer_start(loop, &g_resolve_timeout_watcher); 87 | 88 | // dns_set_tmcbck(g_ctx, dns_timer_setup_cb, loop); 89 | 90 | return sockfd; 91 | } 92 | 93 | void resolve_shutdown(struct ev_loop *loop) { 94 | ev_io_stop(loop, &g_resolve_io_watcher); 95 | 96 | if (ev_is_active(&g_resolve_timeout_watcher)) { 97 | ev_timer_stop(loop, &g_resolve_timeout_watcher); 98 | } 99 | 100 | dns_close(g_ctx); 101 | } 102 | 103 | void resolve_cancel(struct resolve_query_t *query) { 104 | dns_cancel(g_ctx, query->q); 105 | free(query); 106 | } 107 | 108 | static void dns_query_v4_cb(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data) { 109 | struct resolve_query_t *query = (struct resolve_query_t *)data; 110 | 111 | logger_trace("dns_query_v4_cb\n"); 112 | 113 | if (NULL == result) { 114 | logger_error("dns_query_v4_cb result is NULL\n"); 115 | query->cb(NULL, query); 116 | return; 117 | } 118 | 119 | if (result->dnsa4_nrr > 0) { 120 | struct sockaddr_storage storage; 121 | storage.ss_family = AF_INET; 122 | 123 | struct sockaddr_in *addr = (struct sockaddr_in *)&storage; 124 | addr->sin_addr = result->dnsa4_addr[0]; 125 | 126 | query->cb(&storage, query); 127 | } 128 | 129 | free(result); 130 | } 131 | 132 | struct resolve_query_t *resolve_query(char *hostname, resolve_cb *cb, void *data) { 133 | struct resolve_query_t *query = (struct resolve_query_t *)malloc(sizeof(struct resolve_query_t)); 134 | 135 | logger_debug("resolve_query [%s]\n", hostname); 136 | 137 | if (NULL == query) { 138 | logger_error("malloc fail, errno: [%d]\n", errno); 139 | return NULL; 140 | } 141 | 142 | memset(query, 0, sizeof(struct resolve_query_t)); 143 | query->data = data; 144 | query->cb = cb; 145 | query->q = dns_submit_a4(g_ctx, hostname, 0, dns_query_v4_cb, query); 146 | if (NULL == query->q) { 147 | logger_error("dns_submit_a4 fail, errno: [%d]\n", errno); 148 | free(query); 149 | return NULL; 150 | } 151 | 152 | return query; 153 | } 154 | -------------------------------------------------------------------------------- /src/resolve.h: -------------------------------------------------------------------------------- 1 | #ifndef RESOLVE_H 2 | #define RESOLVE_H 3 | 4 | #include 5 | #include 6 | 7 | struct resolve_query_t; 8 | 9 | typedef void resolve_cb(struct sockaddr_storage *storage, struct resolve_query_t *query); 10 | struct resolve_query_t { 11 | struct dns_query *q; 12 | resolve_cb *cb; 13 | void *data; 14 | }; 15 | 16 | int resolve_init(struct ev_loop *loop, char **nameservers, int nameserver_num); 17 | void resolve_shutdown(struct ev_loop *loop); 18 | void resolve_cancel(struct resolve_query_t *query); 19 | 20 | struct resolve_query_t *resolve_query(char *hostname, resolve_cb *cb, void *data); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/socks5.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "socks5.h" 5 | #include "callback.h" 6 | #include "logger.h" 7 | 8 | struct socks5_conn *socks5_conn_new() { 9 | struct socks5_conn *conn = (struct socks5_conn *)malloc(sizeof(struct socks5_conn)); 10 | if (NULL == conn) { 11 | goto _clean; 12 | } 13 | 14 | memset(conn, 0, sizeof(struct socks5_conn)); 15 | 16 | conn->stage = SOCKS5_CONN_STAGE_EXMETHOD; 17 | 18 | conn->client.input = buffer_new(SOCKS5_DEFAULT_BUFFER_SIZE); 19 | if (NULL == conn->client.input) { 20 | goto _clean; 21 | } 22 | conn->client.output = buffer_new(SOCKS5_DEFAULT_BUFFER_SIZE); 23 | if (NULL == conn->client.output) { 24 | goto _clean; 25 | } 26 | conn->client.rw = (struct ev_io *)malloc(sizeof(struct ev_io)); 27 | if (NULL == conn->client.rw) { 28 | goto _clean; 29 | } 30 | conn->client.rw->data = conn; 31 | conn->client.ww = (struct ev_io *)malloc(sizeof(struct ev_io)); 32 | if (NULL == conn->client.ww) { 33 | goto _clean; 34 | } 35 | conn->client.ww->data = conn; 36 | 37 | conn->remote.input = buffer_new(SOCKS5_DEFAULT_BUFFER_SIZE); 38 | if (NULL == conn->remote.input) { 39 | goto _clean; 40 | } 41 | conn->remote.output = buffer_new(SOCKS5_DEFAULT_BUFFER_SIZE); 42 | if (NULL == conn->remote.output) { 43 | goto _clean; 44 | } 45 | conn->remote.bndaddr = buffer_new(SOCKS5_DEFAULT_BUFFER_SIZE); 46 | if (NULL == conn->remote.bndaddr) { 47 | goto _clean; 48 | } 49 | conn->remote.rw = (struct ev_io *)malloc(sizeof(struct ev_io)); 50 | if (NULL == conn->remote.rw) { 51 | goto _clean; 52 | } 53 | conn->remote.rw->data = conn; 54 | conn->remote.ww = (struct ev_io *)malloc(sizeof(struct ev_io)); 55 | if (NULL == conn->remote.ww) { 56 | goto _clean; 57 | } 58 | conn->remote.ww->data = conn; 59 | 60 | return conn; 61 | 62 | _clean: 63 | if (conn) { 64 | socks5_conn_close(conn); 65 | } 66 | return NULL; 67 | } 68 | 69 | void socks5_conn_close(struct socks5_conn *conn) { 70 | struct ev_loop *loop = conn->loop; 71 | 72 | conn->stage = SOCKS5_CONN_STAGE_CLOSED; 73 | 74 | if (conn->client.fd) ev_io_stop(loop, conn->client.rw); 75 | if (conn->client.fd) ev_io_stop(loop, conn->client.ww); 76 | if (conn->remote.fd) ev_io_stop(loop, conn->remote.rw); 77 | if (conn->remote.fd) ev_io_stop(loop, conn->remote.ww); 78 | 79 | if (conn->client.input) { 80 | buffer_free(conn->client.input); 81 | } 82 | if (conn->client.output) { 83 | buffer_free(conn->client.output); 84 | } 85 | if (conn->client.rw) { 86 | free(conn->client.rw); 87 | } 88 | if (conn->client.ww) { 89 | free(conn->client.ww); 90 | } 91 | if (conn->client.fd) { 92 | close(conn->client.fd); 93 | } 94 | 95 | if (conn->remote.input) { 96 | buffer_free(conn->remote.input); 97 | } 98 | if (conn->remote.output) { 99 | buffer_free(conn->remote.output); 100 | } 101 | if (conn->remote.bndaddr) { 102 | buffer_free(conn->remote.bndaddr); 103 | } 104 | if (conn->remote.rw) { 105 | free(conn->remote.rw); 106 | } 107 | if (conn->remote.ww) { 108 | free(conn->remote.ww); 109 | } 110 | if (conn->remote.fd) { 111 | close(conn->remote.fd); 112 | } 113 | free(conn); 114 | } 115 | 116 | void socks5_conn_setstage(struct socks5_conn *conn, uint8_t stage) { 117 | logger_debug("change stage from [%d] to [%d]\n", conn->stage, stage); 118 | conn->stage = stage; 119 | } 120 | -------------------------------------------------------------------------------- /src/socks5.h: -------------------------------------------------------------------------------- 1 | #ifndef SOCKS5_H 2 | #define SOCKS5_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "buffer.h" 9 | #include "resolve.h" 10 | 11 | #pragma pack(1) 12 | 13 | // socks5 version 14 | #define SOCKS5_VERSION 0x05 15 | 16 | // socks5 reserved 17 | #define SOCKS5_RSV 0x00 18 | 19 | // socks5 auth method 20 | #define SOCKS5_AUTH_NOAUTH 0x00 21 | #define SOCKS5_AUTH_USERNAMEPASSWORD 0x02 22 | #define SOCKS5_AUTH_NOACCEPTABLE 0xff 23 | 24 | struct socks5_method_req { 25 | uint8_t ver; 26 | uint8_t nmethods; 27 | uint8_t methods[0]; 28 | }; 29 | 30 | struct socks5_method_res { 31 | uint8_t ver; 32 | uint8_t method; 33 | }; 34 | 35 | // socks5 command 36 | #define SOCKS5_CMD_CONNECT 0x01 37 | #define SOCKS5_CMD_BIND 0x02 38 | #define SOCKS5_CMD_UDPASSOCIATE 0x03 39 | 40 | // socks5 address type 41 | #define SOCKS5_ADDRTYPE_IPV4 0x01 42 | #define SOCKS5_ADDRTYPE_DOMAIN 0x03 43 | #define SOCKS5_ADDRTYPE_IPV6 0x04 44 | 45 | struct socks5_request { 46 | uint8_t ver; 47 | uint8_t cmd; 48 | uint8_t rsv; 49 | uint8_t addrtype; 50 | }; 51 | 52 | // socks5 response status 53 | #define SOCKS5_RESPONSE_SUCCESS 0x00 54 | #define SOCKS5_RESPONSE_SERVER_FAILURE 0x01 55 | #define SOCKS5_RESPONSE_CONNECTION_NOT_ALLOWED 0x02 56 | #define SOCKS5_RESPONSE_NETWORK_UNREACHABLE 0x03 57 | #define SOCKS5_RESPONSE_HOST_UNREACHABLE 0x04 58 | #define SOCKS5_RESPONSE_CONNECTION_REFUSED 0x05 59 | #define SOCKS5_RESPONSE_TTL_EXPIRED 0x06 60 | #define SOCKS5_RESPONSE_COMMAND_NOT_SUPPORTED 0x07 61 | #define SOCKS5_RESPONSE_ADDRTYPE_NOT_SUPPORTED 0x08 62 | 63 | struct socks5_response { 64 | uint8_t ver; 65 | uint8_t rep; 66 | uint8_t rsv; 67 | uint8_t addrtype; 68 | }; 69 | 70 | #define SOCKS5_AUTH_USERNAMEPASSWORD_VER 0x01 71 | 72 | #define SOCKS5_AUTH_USERNAMEPASSWORD_MAX_LEN 256 73 | struct socks5_userpass_req { 74 | uint8_t ver; 75 | uint8_t ulen; 76 | char username[SOCKS5_AUTH_USERNAMEPASSWORD_MAX_LEN]; 77 | uint8_t plen; 78 | char password[SOCKS5_AUTH_USERNAMEPASSWORD_MAX_LEN]; 79 | }; 80 | 81 | #define SOCKS5_AUTH_USERNAMEPASSWORD_STATUS_OK 0x00 82 | #define SOCKS5_AUTH_USERNAMEPASSWORD_STATUS_FAIL 0x01 83 | struct socks5_userpass_res { 84 | uint8_t ver; 85 | uint8_t status; 86 | }; 87 | 88 | #pragma pack() 89 | 90 | #define SOCKS5_DEFAULT_BUFFER_SIZE 128 91 | 92 | struct socks5_client_conn { 93 | int fd; 94 | struct ev_io *rw; // read watcher 95 | struct ev_io *ww; // write watcher 96 | buffer_t *input; 97 | buffer_t *output; 98 | }; 99 | 100 | struct socks5_remote_conn { 101 | int fd; 102 | char hostname[DNS_MAXNAME]; 103 | uint16_t port; 104 | struct resolve_query_t *query; 105 | struct ev_io *rw; // read watcher 106 | struct ev_io *ww; // write watcher 107 | buffer_t *input; 108 | buffer_t *output; 109 | uint8_t addrtype; 110 | buffer_t *bndaddr; 111 | }; 112 | 113 | struct socks5_server { 114 | size_t ulen; 115 | char username[SOCKS5_AUTH_USERNAMEPASSWORD_MAX_LEN]; 116 | size_t plen; 117 | char password[SOCKS5_AUTH_USERNAMEPASSWORD_MAX_LEN]; 118 | uint16_t port; 119 | uint8_t auth_method; 120 | bool daemon; 121 | uint8_t log_level; 122 | }; 123 | 124 | struct socks5_conn { 125 | struct socks5_server *server; 126 | struct socks5_client_conn client; 127 | struct socks5_remote_conn remote; 128 | #define SOCKS5_CONN_STAGE_EXMETHOD 1 129 | #define SOCKS5_CONN_STAGE_USERNAMEPASSWORD 2 130 | #define SOCKS5_CONN_STAGE_EXHOST 3 131 | #define SOCKS5_CONN_STAGE_DNSQUERY 4 132 | #define SOCKS5_CONN_STAGE_CONNECTING 5 133 | #define SOCKS5_CONN_STAGE_CONNECTED 6 134 | #define SOCKS5_CONN_STAGE_STREAM 7 135 | #define SOCKS5_CONN_STAGE_CLOSING 8 136 | #define SOCKS5_CONN_STAGE_CLOSED 9 137 | uint8_t stage; 138 | uint8_t method; 139 | struct ev_loop *loop; 140 | }; 141 | 142 | struct socks5_conn *socks5_conn_new(); 143 | void socks5_conn_close(); 144 | 145 | void socks5_conn_setstage(struct socks5_conn *conn, uint8_t stage); 146 | 147 | #endif 148 | -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | TEST_URL='http://httpbin.org/ip' 4 | CURL_ADDR_IPV4="curl -s -x socks5://localhost:23456" 5 | CURL_ADDR_DOMAIN="curl -s -x socks5h://localhost:23456" 6 | 7 | expected=$(curl -s $TEST_URL) 8 | 9 | SUCCESS_CONDS=( 10 | # noauth 11 | "$CURL_ADDR_IPV4 $TEST_URL" 12 | # username/password 13 | "$CURL_ADDR_IPV4 -U uusername:ppassword $TEST_URL" 14 | # proxy dns 15 | "$CURL_ADDR_DOMAIN $TEST_URL" 16 | ) 17 | 18 | for cmd in "${SUCCESS_CONDS[@]}"; do 19 | real=$($cmd) 20 | if [ "$expected" == "$real" ]; then 21 | echo "passed: " $cmd 22 | else 23 | echo "fail: " $cmd 24 | fi 25 | done 26 | --------------------------------------------------------------------------------