├── .gitignore ├── COPYING ├── Makefile ├── README.org ├── bin ├── AA │ └── shadowsocks-client_0.5-d8ef02715f40de0fb7ba0f7267d3f8260f38ba80_ar71xx.ipk └── current │ └── shadowsocks-client_0.5-d8ef02715f40de0fb7ba0f7267d3f8260f38ba80_ar71xx.ipk ├── client.c ├── common.c ├── common.h ├── crypto.c ├── crypto.h ├── log.c ├── log.h ├── packages ├── shadowsocks-client-AA │ ├── Makefile │ └── files │ │ ├── sslocal.config │ │ └── sslocal.init └── shadowsocks-client │ ├── Makefile │ └── files │ ├── sslocal.config │ └── sslocal.init ├── server.c └── test.c /.gitignore: -------------------------------------------------------------------------------- 1 | # executable file 2 | sslocal 3 | sserver 4 | test 5 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Zhao, Gang 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -g -Wall 2 | 3 | .PHONY: all 4 | all: sslocal sserver test 5 | 6 | sslocal : client.c common.o crypto.o log.o 7 | $(CC) -o $@ $(CFLAGS) $^ $(LDFLAGS) -lcrypto 8 | 9 | sserver : server.c common.o crypto.o log.o 10 | $(CC) -o $@ $(CFLAGS) $^ $(LDFLAGS) -lcrypto 11 | 12 | test: test.c common.o crypto.o log.o 13 | $(CC) -o $@ $(CFLAGS) $^ $(LDFLAGS) -lcrypto 14 | 15 | common.o: common.h 16 | 17 | crypto.o: crypto.h 18 | 19 | log.o: log.h 20 | 21 | .PHONY: clean 22 | clean: 23 | rm -rf *.o sserver sslocal test 24 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * shadowsocks-tiny 2 | 3 | ** What 4 | This is a tiny [[https://github.com/clowwindy/shadowsocks/wiki][shadowsocks]] client for router(OpenWrt). 5 | 6 | ** Install 7 | Pre-compiled .ipk packages can be found in =bin= directory of the 8 | shadowsocks-tiny source code. Subdirectory =AA= is for Attitude 9 | Adjustment(12.09), subdirectory =current= is for latest OpenWrt 10 | release(which uses [[http://wiki.openwrt.org/doc/techref/procd][procd]]). If you can't find the package for your 11 | router's architecture, you need to compile it by yourself(and if 12 | you successfully compiled and tested it works, please send the .ipk 13 | to me, so I can include it here). 14 | 15 | - Compile from SDK 16 | 17 | There is a help page about using the SDK: 18 | [[http://wiki.openwrt.org/doc/howto/obtain.firmware.sdk]] 19 | 20 | Shadowsocks-tiny depends on libopenssl. If libopenssl has not 21 | been installed on your router, you have to install libopenssl 22 | first. Usually you only need to run following command on your 23 | router to install libopenssl: 24 | #+begin_src shell 25 | opkg install libopenssl 26 | #+end_src 27 | 28 | Packages are under =packages= directory of the shadowsocks-tiny 29 | source code. =./packages/shadowsocks-client-AA= is for Attitude 30 | Adjustment(12.09), and =./packages/shadowsocks-client= is for 31 | latest OpenWrt(which uses [[http://wiki.openwrt.org/doc/techref/procd][procd]]). Actually the only difference of 32 | these two packages is the init script. Select the right package 33 | for your SDK, and copy it to =packages= directory of your SDK, then 34 | compile. 35 | 36 | - Compile from OpenWrt souce 37 | 38 | You can add shadowsocks-client from packages feed: 39 | #+begin_src shell 40 | ./scripts/feeds update packages 41 | ./scripts/feeds install shadowsocks-client 42 | #+end_src 43 | 44 | Then select shadowsocks-client from =make menuconfig= and compile. 45 | 46 | ** Config 47 | Shadowsocks-client uses [[http://wiki.openwrt.org/doc/uci][uci]] as its configuration method. You have 48 | to edit =/etc/config/sslocal= to meet your needs. 49 | 50 | Hint: =server_addr= and =server_port= is your shadowsocks server's 51 | listening address and port. =local_addr= is your router's 52 | address(e.g. 192.168.X.X) and =local_port= is the port number you 53 | want your router to listen. 54 | 55 | After editing =/etc/config/sslocal=, run =/etc/init.d/sslocal 56 | start= to execute sslocal, and use =logread= to see if it works. 57 | Normally it will show: 58 | 59 | Wed Jul 16 03:17:28 2014 daemon.notice sslocal: server address: XXX.XXX.XXX.XXX:XXXXX(tcp) 60 | 61 | Wed Jul 16 03:17:28 2014 daemon.notice sslocal: listening address: XXX.XXX.XXX.XXX:XXXXX(tcp) 62 | 63 | If all seem okay, run =/etc/init.d/sslocal enable= to let 64 | shadowsocks-client automatically starts when your router boots. 65 | 66 | - Host setting 67 | 68 | Set your browser or program to use socks5 proxy, and put 69 | =local_addr= and =local_port= as the socks5 server's addresss and 70 | port number. 71 | 72 | ** Note 73 | Although shadowsocks-tiny has a server side program, it's mainly 74 | for test purpose and doesn't scale well. You can find other fancy 75 | server side programs of shadowsocks from 76 | [[https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients]] 77 | 78 | ** Report bugs 79 | Please submit your bug report to 80 | [[https://github.com/zhao-gang/shadowsocks-tiny/issues]] 81 | 82 | ** License 83 | MIT, see COPYING for details. 84 | -------------------------------------------------------------------------------- /bin/AA/shadowsocks-client_0.5-d8ef02715f40de0fb7ba0f7267d3f8260f38ba80_ar71xx.ipk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhao-gang/shadowsocks-tiny/f26951069e15fde443e2404e005e15445e1301fa/bin/AA/shadowsocks-client_0.5-d8ef02715f40de0fb7ba0f7267d3f8260f38ba80_ar71xx.ipk -------------------------------------------------------------------------------- /bin/current/shadowsocks-client_0.5-d8ef02715f40de0fb7ba0f7267d3f8260f38ba80_ar71xx.ipk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhao-gang/shadowsocks-tiny/f26951069e15fde443e2404e005e15445e1301fa/bin/current/shadowsocks-client_0.5-d8ef02715f40de0fb7ba0f7267d3f8260f38ba80_ar71xx.ipk -------------------------------------------------------------------------------- /client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Zhao, Gang 3 | * This is free software; you can redistribute it and/or modify 4 | * it under the terms of the MIT license. See COPYING for details. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "common.h" 19 | #include "crypto.h" 20 | #include "log.h" 21 | 22 | char rsv_frag[3] = {0x00, 0x00, 0x00}; 23 | 24 | int parse_socks5_proto(int sockfd, struct link *ln) 25 | { 26 | int ret, cmd; 27 | 28 | if (!(ln->state & SOCKS5_AUTH_REQUEST_RECEIVED)) { 29 | ln->state |= SOCKS5_AUTH_REQUEST_RECEIVED; 30 | 31 | if (check_socks5_auth_header(sockfd, ln) == -1) { 32 | if (create_socks5_auth_reply(sockfd, ln, 0) == -1) 33 | goto out; 34 | } else { 35 | if (create_socks5_auth_reply(sockfd, ln, 1) == -1) 36 | goto out; 37 | } 38 | 39 | ret = do_send(sockfd, ln, "text", 0); 40 | if (ret == -2) { 41 | goto out; 42 | } else if (ret == -1) { 43 | ln->state |= LOCAL_SEND_PENDING; 44 | return 0; 45 | } else { 46 | ln->state |= SOCKS5_AUTH_REPLY_SENT; 47 | return 0; 48 | } 49 | } else if (!(ln->state & SOCKS5_CMD_REQUEST_RECEIVED)) { 50 | ln->state |= SOCKS5_CMD_REQUEST_RECEIVED; 51 | 52 | if (check_socks5_cmd_header(sockfd, ln) == -1) { 53 | cmd = SOCKS5_CMD_REP_FAILED; 54 | if (create_socks5_cmd_reply(sockfd, ln, cmd) == -1) 55 | goto out; 56 | } else { 57 | cmd = SOCKS5_CMD_REP_SUCCEEDED; 58 | if (create_socks5_cmd_reply(sockfd, ln, cmd) == -1) 59 | goto out; 60 | } 61 | 62 | /* cmd reply to local */ 63 | ret = do_send(sockfd, ln, "text", 0); 64 | if (ret == -2 || cmd == SOCKS5_CMD_REP_FAILED) { 65 | goto out; 66 | } else if (ret == -1) { 67 | ln->state |= LOCAL_SEND_PENDING; 68 | return 0; 69 | } else { 70 | ln->state |= SOCKS5_CMD_REPLY_SENT; 71 | return 0; 72 | } 73 | } 74 | 75 | return 0; 76 | out: 77 | return -1; 78 | } 79 | 80 | /* read text from local, encrypt and send to server */ 81 | int client_do_local_read(int sockfd, struct link *ln) 82 | { 83 | int ret; 84 | 85 | if (ln->state & LOCAL_SEND_PENDING) 86 | return 0; 87 | 88 | ret = do_read(sockfd, ln, "text", 0); 89 | if (ret == -2) { 90 | goto out; 91 | } else if (ret == -1) { 92 | return 0; 93 | } 94 | 95 | if (!(ln->state & SOCKS5_CMD_REPLY_SENT)) { 96 | if (parse_socks5_proto(sockfd, ln) == -1) 97 | goto out; 98 | 99 | if (ln->state & LOCAL_SEND_PENDING || 100 | ln->text_len == 0) 101 | return 0; 102 | } 103 | 104 | if (ln->state & SS_UDP) { 105 | /* remove rsv(2) + frag(1) */ 106 | if (rm_data(sockfd, ln, "text", 3) == -1) 107 | goto out; 108 | } else if (!(ln->state & SS_TCP_HEADER_SENT)) { 109 | if (add_data(sockfd, ln, "text", 110 | ln->cipher, ln->ss_header_len) == -1) 111 | goto out; 112 | } 113 | 114 | if (crypto_encrypt(sockfd, ln) == -1) 115 | goto out; 116 | 117 | ret = do_send(ln->server_sockfd, ln, "cipher", 0); 118 | if (ret == -2) { 119 | goto out; 120 | } else if (ret == -1) { 121 | ln->state |= SERVER_SEND_PENDING; 122 | } else { 123 | if (!(ln->state & SS_TCP_HEADER_SENT)) 124 | ln->state |= SS_TCP_HEADER_SENT; 125 | } 126 | 127 | return 0; 128 | out: 129 | return -1; 130 | } 131 | 132 | /* read cipher from server, decrypt and send to local */ 133 | int client_do_server_read(int sockfd, struct link *ln) 134 | { 135 | int ret; 136 | 137 | if (ln->state & SERVER_SEND_PENDING) { 138 | return 0; 139 | } 140 | 141 | /* if iv isn't received, wait to receive bigger than iv_len 142 | * bytes before go to next step */ 143 | if (ln->state & SERVER_READ_PENDING) { 144 | sock_debug(sockfd, "%s: server read pending", __func__); 145 | pr_link_debug(ln); 146 | 147 | ret = do_read(sockfd, ln, "cipher", ln->cipher_len); 148 | if (ret == -2) { 149 | goto out; 150 | } else if (ret == -1) { 151 | return 0; 152 | } 153 | 154 | if (ln->cipher_len <= iv_len) { 155 | return 0; 156 | } else { 157 | ln->state &= ~SERVER_READ_PENDING; 158 | } 159 | } else { 160 | ret = do_read(sockfd, ln, "cipher", 0); 161 | if (ret == -2) { 162 | goto out; 163 | } else if (ret == -1) { 164 | return 0; 165 | } 166 | 167 | if (!(ln->state & SS_IV_RECEIVED)) { 168 | if (ln->cipher_len <= iv_len) { 169 | ln->state |= SERVER_READ_PENDING; 170 | return 0; 171 | } 172 | } 173 | } 174 | 175 | if (crypto_decrypt(sockfd, ln) == -1) 176 | goto out; 177 | 178 | if (ln->state & SS_UDP) { 179 | if (add_data(sockfd, ln, "text", 180 | rsv_frag, sizeof(rsv_frag)) == -1) 181 | goto out; 182 | } 183 | 184 | ret = do_send(ln->local_sockfd, ln, "text", 0); 185 | if (ret == -2) { 186 | goto out; 187 | } else if (ret == -1) { 188 | ln->state |= LOCAL_SEND_PENDING; 189 | } 190 | 191 | return 0; 192 | out: 193 | return -1; 194 | } 195 | 196 | int client_do_pollin(int sockfd, struct link *ln) 197 | { 198 | if (sockfd == ln->local_sockfd) { 199 | if (ln->state & SERVER_PENDING) { 200 | sock_debug(sockfd, "%s: server pending", 201 | __func__); 202 | goto out; 203 | } else if (client_do_local_read(sockfd, ln) == -1) { 204 | goto clean; 205 | } 206 | } else if (sockfd == ln->server_sockfd) { 207 | if (ln->state & LOCAL_PENDING) { 208 | sock_debug(sockfd, "%s: local pending", 209 | __func__); 210 | goto out; 211 | } else if (client_do_server_read(sockfd, ln) == -1) { 212 | goto clean; 213 | } 214 | } 215 | 216 | out: 217 | return 0; 218 | clean: 219 | sock_info(sockfd, "%s close", __func__); 220 | destroy_link(sockfd); 221 | return -1; 222 | } 223 | 224 | int client_do_pollout(int sockfd, struct link *ln) 225 | { 226 | int ret, optval; 227 | int optlen = sizeof(optval); 228 | 229 | /* write to local */ 230 | if (sockfd == ln->local_sockfd) { 231 | if (ln->state & LOCAL_SEND_PENDING) { 232 | ret = do_send(sockfd, ln, "text", 0); 233 | if (ret == -2) { 234 | goto clean; 235 | } else if (ret == -1) { 236 | goto out; 237 | } else { 238 | ln->state &= ~LOCAL_SEND_PENDING; 239 | } 240 | 241 | /* update socks5 state */ 242 | if (!(ln->state & SOCKS5_AUTH_REPLY_SENT)) 243 | ln->state &= SOCKS5_AUTH_REPLY_SENT; 244 | else if (!(ln->state & SOCKS5_CMD_REPLY_SENT)) 245 | ln->state &= SOCKS5_CMD_REPLY_SENT; 246 | 247 | goto out; 248 | } else { 249 | poll_rm(sockfd, POLLOUT); 250 | } 251 | } else if (sockfd == ln->server_sockfd) { 252 | /* pending connect finished */ 253 | if (!(ln->state & SERVER)) { 254 | if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, 255 | &optval, (void *)&optlen) == -1) { 256 | sock_warn(sockfd, "%s: getsockopt() %s", 257 | __func__, strerror(errno)); 258 | goto clean; 259 | } 260 | 261 | if (optval == 0) { 262 | sock_info(sockfd, 263 | "%s: pending connect() finished", 264 | __func__); 265 | ln->time = time(NULL); 266 | ln->state |= SERVER; 267 | } else { 268 | sock_warn(sockfd, 269 | "%s: pending connect() failed", 270 | __func__); 271 | goto clean; 272 | } 273 | } 274 | 275 | if (ln->state & SERVER_SEND_PENDING) { 276 | /* write to server */ 277 | ret = do_send(sockfd, ln, "cipher", 0); 278 | if (ret == -2) { 279 | goto clean; 280 | } else if (ret == -1) { 281 | goto out; 282 | } else { 283 | ln->state &= ~SERVER_SEND_PENDING; 284 | 285 | if (!(ln->state & SS_TCP_HEADER_SENT)) 286 | ln->state |= SS_TCP_HEADER_SENT; 287 | goto out; 288 | } 289 | } else { 290 | poll_rm(sockfd, POLLOUT); 291 | } 292 | } 293 | 294 | out: 295 | return 0; 296 | clean: 297 | sock_info(sockfd, "%s: close", __func__); 298 | destroy_link(sockfd); 299 | return -1; 300 | } 301 | 302 | int main(int argc, char **argv) 303 | { 304 | short revents; 305 | int i, listenfd, sockfd; 306 | int ret = 0; 307 | struct link *ln; 308 | struct addrinfo *server_ai = NULL; 309 | struct addrinfo *local_ai = NULL; 310 | struct addrinfo hint; 311 | 312 | check_ss_option(argc, argv, "client"); 313 | 314 | memset(&hint, 0, sizeof(hint)); 315 | hint.ai_family = AF_UNSPEC; 316 | hint.ai_socktype = SOCK_STREAM; 317 | 318 | ret = getaddrinfo(ss_opt.server_addr, ss_opt.server_port, 319 | &hint, &server_ai); 320 | if (ret != 0) { 321 | pr_warn("getaddrinfo error: %s\n", gai_strerror(ret)); 322 | goto out; 323 | } 324 | 325 | pr_ai_notice(server_ai, "server address"); 326 | 327 | ret = getaddrinfo(ss_opt.local_addr, ss_opt.local_port, &hint, &local_ai); 328 | if (ret != 0) { 329 | pr_warn("getaddrinfo error: %s\n", gai_strerror(ret)); 330 | goto out; 331 | } 332 | 333 | pr_ai_notice(local_ai, "listening address"); 334 | 335 | if (crypto_init(ss_opt.password, ss_opt.method) == -1) { 336 | ret = -1; 337 | goto out; 338 | } 339 | 340 | ss_init(); 341 | listenfd = do_listen(local_ai, "tcp"); 342 | clients[0].fd = listenfd; 343 | clients[0].events = POLLIN; 344 | 345 | while (1) { 346 | pr_debug("start polling\n"); 347 | ret = poll(clients, nfds, TCP_INACTIVE_TIMEOUT * 1000); 348 | if (ret == -1) 349 | err_exit("poll error"); 350 | else if (ret == 0) { 351 | reaper(); 352 | continue; 353 | } 354 | 355 | if (clients[0].revents & POLLIN) { 356 | sockfd = accept(clients[0].fd, NULL, NULL); 357 | if (sockfd == -1) { 358 | pr_warn("accept error\n"); 359 | } else if (poll_set(sockfd, POLLIN) == -1) { 360 | close(sockfd); 361 | } else { 362 | ln = create_link(sockfd, "client"); 363 | if (ln == NULL) { 364 | poll_del(sockfd); 365 | close(sockfd); 366 | } else { 367 | ln->server = server_ai; 368 | } 369 | } 370 | } 371 | 372 | for (i = 1; i < nfds; i++) { 373 | sockfd = clients[i].fd; 374 | if (sockfd == -1) 375 | continue; 376 | 377 | revents = clients[i].revents; 378 | if (revents == 0) 379 | continue; 380 | 381 | ln = get_link(sockfd); 382 | if (ln == NULL) { 383 | sock_warn(sockfd, "close: can't get link"); 384 | close(sockfd); 385 | continue; 386 | } 387 | 388 | if (revents & POLLIN) { 389 | client_do_pollin(sockfd, ln); 390 | } 391 | 392 | if (revents & POLLOUT) { 393 | client_do_pollout(sockfd, ln); 394 | } 395 | 396 | /* suppress the noise */ 397 | /* if (revents & POLLPRI) { */ 398 | /* sock_warn(sockfd, "POLLPRI"); */ 399 | /* } else if (revents & POLLERR) { */ 400 | /* sock_warn(sockfd, "POLLERR"); */ 401 | /* } else if (revents & POLLHUP) { */ 402 | /* sock_warn(sockfd, "POLLHUP"); */ 403 | /* } else if (revents & POLLNVAL) { */ 404 | /* sock_warn(sockfd, "POLLNVAL"); */ 405 | /* } */ 406 | } 407 | 408 | reaper(); 409 | } 410 | 411 | out: 412 | crypto_exit(); 413 | 414 | if (server_ai) 415 | freeaddrinfo(server_ai); 416 | 417 | if (local_ai) 418 | freeaddrinfo(local_ai); 419 | 420 | ss_exit(); 421 | 422 | if (ret == -1) 423 | exit(EXIT_FAILURE); 424 | else 425 | exit(EXIT_SUCCESS); 426 | } 427 | -------------------------------------------------------------------------------- /common.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Zhao, Gang 3 | * This is free software; you can redistribute it and/or modify 4 | * it under the terms of the MIT license. See COPYING for details. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "log.h" 27 | #include "common.h" 28 | 29 | static bool daemonize; 30 | int nfds = DEFAULT_MAX_CONNECTION; 31 | struct pollfd *clients; 32 | struct ss_option ss_opt; 33 | struct link **link_head; 34 | 35 | static void usage_client(const char *name) 36 | { 37 | pr_err("Usage: %s [options]\n" 38 | "Options:\n" 39 | "\t-s,--server_addr server address\n" 40 | "\t-p,--server_port server port\n" 41 | "\t-u,--local_addr\t local Used address\n" 42 | "\t-b,--local_port\t local Binding port\n" 43 | "\t-k,--password\t your password\n" 44 | "\t-m,--method\t encryption algorithm(aes-*-cfb, bf-cfb, cast5-cfb, des-cfb, rc2-cfb, rc4, seed-cfb)\n" 45 | "\t-d,--daemon\t run as daemon\n" 46 | "\t-l,--log_level\t log level(0-7), default is LOG_NOTICE\n" 47 | "\t-h,--help\t print this help\n", name); 48 | } 49 | 50 | static void usage_server(const char *name) 51 | { 52 | pr_err("Usage: %s [options]\n" 53 | "Options:\n" 54 | "\t-u,--local_addr\t local address\n" 55 | "\t-b,--local_port\t local port\n" 56 | "\t-k,--password\t your password\n" 57 | "\t-m,--method\t encryption algorithm\n" 58 | "\t-d,--daemon\t run as daemon\n" 59 | "\t-l,--log_level\t log level(0-7), default is LOG_NOTICE\n" 60 | "\t-h,--help\t print this help information\n", name); 61 | } 62 | 63 | static void pr_ss_option(const char *type) 64 | { 65 | char *server = NULL; 66 | char *server_port = NULL; 67 | 68 | if (strcmp(type, "client") == 0) { 69 | server = ss_opt.server_addr; 70 | server_port = ss_opt.server_port; 71 | } 72 | 73 | pr_info("Options:\n" 74 | "server address: %s, server port: %s\n" 75 | "local address: %s, local port: %s\n" 76 | "password: %s\n" 77 | "method: %s\n", 78 | server, server_port, 79 | ss_opt.local_addr, ss_opt.local_port, 80 | ss_opt.password, ss_opt.method); 81 | } 82 | 83 | static void parse_cmdline(int argc, char **argv, const char *type) 84 | { 85 | int len, opt; 86 | int log_opt = LOG_CONS | LOG_PERROR; 87 | int level = -1; 88 | char missing[128] = ""; 89 | struct option *longopts; 90 | const char *optstring; 91 | void (*usage)(const char *name); 92 | struct option server_long_options[] = { 93 | {"local_addr", required_argument, 0, 'u'}, 94 | {"local_port", required_argument, 0, 'b'}, 95 | {"password", required_argument, 0, 'k'}, 96 | {"method", required_argument, 0, 'm'}, 97 | {"daemon", no_argument, 0, 'd'}, 98 | {"log_level", no_argument, 0, 'l'}, 99 | {"help", no_argument, 0, 'h'}, 100 | {0, 0, 0, 0} 101 | }; 102 | 103 | struct option client_long_options[] = { 104 | {"server_addr", required_argument, 0, 's'}, 105 | {"server_port", required_argument, 0, 'p'}, 106 | {"local_addr", required_argument, 0, 'u'}, 107 | {"local_port", required_argument, 0, 'b'}, 108 | {"password", required_argument, 0, 'k'}, 109 | {"method", required_argument, 0, 'm'}, 110 | {"daemon", no_argument, 0, 'd'}, 111 | {"log_level", no_argument, 0, 'l'}, 112 | {"log_stderr", no_argument, 0, 'L'}, 113 | {"help", no_argument, 0, 'h'}, 114 | {0, 0, 0, 0} 115 | }; 116 | 117 | if (strcmp(type, "client") == 0) { 118 | longopts = client_long_options; 119 | optstring = "s:p:u:b:k:m:dl:h"; 120 | usage = usage_client; 121 | openlog("sslocal", log_opt, LOG_DAEMON); 122 | } else if (strcmp(type, "server") == 0) { 123 | longopts = server_long_options; 124 | optstring = "u:b:k:m:dl:h"; 125 | usage = usage_server; 126 | openlog("sserver", log_opt, LOG_DAEMON); 127 | } else { 128 | openlog("shadowsocks", log_opt, LOG_DAEMON); 129 | pr_exit("%s: unknown type\n", __func__); 130 | } 131 | 132 | while (1) { 133 | opt = getopt_long(argc, argv, optstring, longopts, NULL); 134 | if (opt == -1) 135 | break; 136 | 137 | switch (opt) { 138 | case 's': 139 | if (strcmp(type, "server") == 0) { 140 | pr_warn("%s: server doesn't need -s option\n", 141 | __func__); 142 | break; 143 | } 144 | 145 | len = strlen(optarg); 146 | if (len <= MAX_DOMAIN_LEN) { 147 | strcpy(ss_opt.server_addr, optarg); 148 | } else { 149 | strncpy(ss_opt.server_addr, optarg, 150 | MAX_DOMAIN_LEN); 151 | ss_opt.server_addr[MAX_DOMAIN_LEN] = '\0'; 152 | } 153 | 154 | break; 155 | case 'p': 156 | if (strcmp(type, "server") == 0) { 157 | pr_warn("%s: server doesn't need -p option\n", 158 | __func__); 159 | break; 160 | } 161 | 162 | len = strlen(optarg); 163 | if (len <= MAX_PORT_STRING_LEN) { 164 | strcpy(ss_opt.server_port, optarg); 165 | } else { 166 | strncpy(ss_opt.server_port, optarg, 167 | MAX_PORT_STRING_LEN); 168 | ss_opt.server_port[MAX_PORT_STRING_LEN] = '\0'; 169 | } 170 | 171 | break; 172 | case 'u': 173 | len = strlen(optarg); 174 | if (len <= MAX_DOMAIN_LEN) { 175 | strcpy(ss_opt.local_addr, optarg); 176 | } else { 177 | strncpy(ss_opt.local_addr, optarg, 178 | MAX_DOMAIN_LEN); 179 | ss_opt.local_addr[MAX_DOMAIN_LEN] = '\0'; 180 | } 181 | 182 | break; 183 | case 'b': 184 | len = strlen(optarg); 185 | if (len <= MAX_PORT_STRING_LEN) { 186 | strcpy(ss_opt.local_port, optarg); 187 | } else { 188 | strncpy(ss_opt.local_port, optarg, 189 | MAX_PORT_STRING_LEN); 190 | ss_opt.local_port[MAX_PORT_STRING_LEN] = '\0'; 191 | } 192 | 193 | break; 194 | case 'k': 195 | len = strlen(optarg); 196 | if (len <= MAX_PWD_LEN) { 197 | strcpy(ss_opt.password, optarg); 198 | } else { 199 | strncpy(ss_opt.password, optarg, 200 | MAX_PWD_LEN); 201 | ss_opt.password[MAX_PWD_LEN] = '\0'; 202 | } 203 | 204 | break; 205 | case 'm': 206 | len = strlen(optarg); 207 | if (len <= MAX_METHOD_NAME_LEN) { 208 | strcpy(ss_opt.method, optarg); 209 | } else { 210 | strncpy(ss_opt.method, optarg, 211 | MAX_METHOD_NAME_LEN); 212 | ss_opt.method[MAX_METHOD_NAME_LEN] = '\0'; 213 | } 214 | 215 | break; 216 | case 'd': 217 | daemonize = true; 218 | log_opt &= ~LOG_PERROR; 219 | break; 220 | case 'l': 221 | if (strcmp(optarg, "0") == 0) 222 | level = LOG_EMERG; 223 | else if (strcmp(optarg, "1") == 0) 224 | level = LOG_ALERT; 225 | else if (strcmp(optarg, "2") == 0) 226 | level = LOG_CRIT; 227 | else if (strcmp(optarg, "3") == 0) 228 | level = LOG_ERR; 229 | else if (strcmp(optarg, "4") == 0) 230 | level = LOG_WARNING; 231 | else if (strcmp(optarg, "5") == 0) 232 | level = LOG_NOTICE; 233 | else if (strcmp(optarg, "6") == 0) 234 | level = LOG_INFO; 235 | else if (strcmp(optarg, "7") == 0) 236 | level = LOG_DEBUG; 237 | else 238 | level = LOG_NOTICE; 239 | break; 240 | case 'h': 241 | usage(argv[0]); 242 | exit(EXIT_SUCCESS); 243 | case '?': 244 | usage(argv[0]); 245 | exit(EXIT_FAILURE); 246 | } 247 | } 248 | 249 | if (strcmp(type, "client") == 0) { 250 | if (strlen(ss_opt.server_addr) == 0) 251 | strcat(missing, "-s "); 252 | 253 | if (strlen(ss_opt.server_port) == 0) 254 | strcat(missing, "-p "); 255 | } 256 | 257 | if (strlen(ss_opt.local_addr) == 0) 258 | strcat(missing, "-u "); 259 | 260 | if (strlen(ss_opt.local_port) == 0) 261 | strcat(missing, "-b "); 262 | 263 | if (strlen(ss_opt.password) == 0) 264 | strcat(missing, "-k "); 265 | 266 | if (strlen(ss_opt.method) == 0) 267 | strcat(missing, "-m "); 268 | 269 | if (strlen(missing) != 0) { 270 | pr_err("Missing parameter(s): %s\n", missing); 271 | usage(argv[0]); 272 | exit(EXIT_FAILURE); 273 | } 274 | 275 | /* reopen log to user specified level */ 276 | closelog(); 277 | if (strcmp(type, "client") == 0) 278 | openlog("sslocal", log_opt, LOG_DAEMON); 279 | else if (strcmp(type, "server") == 0) 280 | openlog("sserver", log_opt, LOG_DAEMON); 281 | 282 | if (level == -1) 283 | level = LOG_NOTICE; 284 | 285 | setlogmask(LOG_UPTO(level)); 286 | } 287 | 288 | void check_ss_option(int argc, char **argv, const char *type) 289 | { 290 | parse_cmdline(argc, argv, type); 291 | 292 | if (daemonize) { 293 | if (daemon(0, 0) == -1) 294 | pr_exit("daemon failed: %s\n", strerror(errno)); 295 | } 296 | 297 | pr_ss_option(type); 298 | } 299 | 300 | void pr_data(FILE *fp, const char *name, char *data, int len) 301 | { 302 | fprintf(fp, "%s:\n", name); 303 | BIO_dump_fp(fp, (void *)data, len); 304 | } 305 | 306 | void _pr_link(int level, struct link *ln) 307 | { 308 | enum link_state state = ln->state; 309 | char state_str[512] = {'\0'}; 310 | 311 | if (state & LOCAL && state & SERVER) 312 | strcat(state_str, "linked"); 313 | else if (state & LOCAL) 314 | strcat(state_str, "local"); 315 | else if (state & SERVER) 316 | strcat(state_str, "server"); 317 | 318 | if (state & SS_UDP) 319 | strcat(state_str, ", udp"); 320 | 321 | if (state & SS_IV_SENT && state & SS_IV_RECEIVED) 322 | strcat(state_str, ", iv exchanged"); 323 | else if (state & SS_IV_SENT) 324 | strcat(state_str, ", iv sent"); 325 | else if (state & SS_IV_RECEIVED) 326 | strcat(state_str, ", iv received"); 327 | 328 | if (state & SS_TCP_HEADER_SENT) 329 | strcat(state_str, ", ss tcp header sent"); 330 | else if (state & SS_TCP_HEADER_RECEIVED) 331 | strcat(state_str, ", ss tcp header received"); 332 | else if (state & SOCKS5_CMD_REPLY_SENT) 333 | strcat(state_str, ", socks5 cmd reply sent"); 334 | else if (state & SOCKS5_CMD_REQUEST_RECEIVED) 335 | strcat(state_str, ", socks5 cmd request received"); 336 | else if (state & SOCKS5_AUTH_REPLY_SENT) 337 | strcat(state_str, ", socks5 auth reply sent"); 338 | else if (state & SOCKS5_AUTH_REQUEST_RECEIVED) 339 | strcat(state_str, ", socks5 auth request received"); 340 | 341 | if (state & LOCAL_SEND_PENDING) 342 | strcat(state_str, ", local send pending"); 343 | 344 | if (state & LOCAL_READ_PENDING) 345 | strcat(state_str, ", local read pending"); 346 | 347 | if (state & SERVER_READ_PENDING) 348 | strcat(state_str, ", server read pending"); 349 | 350 | if (state & SERVER_SEND_PENDING) 351 | strcat(state_str, ", server_send_pending"); 352 | 353 | syslog(level, "state: %s\n", state_str); 354 | syslog(level, "local sockfd: %d; server sockfd: %d; " 355 | "text len: %d; cipher len: %d;\n", 356 | ln->local_sockfd, ln->server_sockfd, 357 | ln->text_len, ln->cipher_len); 358 | } 359 | 360 | void pr_link_debug(struct link *ln) 361 | { 362 | _pr_link(LOG_DEBUG, ln); 363 | } 364 | 365 | void pr_link_info(struct link *ln) 366 | { 367 | _pr_link(LOG_INFO, ln); 368 | } 369 | 370 | void pr_link_notice(struct link *ln) 371 | { 372 | _pr_link(LOG_NOTICE, ln); 373 | } 374 | 375 | void pr_link_warn(struct link *ln) 376 | { 377 | _pr_link(LOG_WARNING, ln); 378 | } 379 | 380 | void ss_init(void) 381 | { 382 | int i, ret; 383 | struct rlimit limit; 384 | 385 | ret = getrlimit(RLIMIT_NOFILE, &limit); 386 | if (ret == -1) { 387 | pr_err("%s: %s\n", __func__, strerror(errno)); 388 | } else { 389 | if (limit.rlim_cur < DEFAULT_MAX_CONNECTION) 390 | nfds = limit.rlim_cur; 391 | } 392 | 393 | pr_info("%s: max connection: %d\n", __func__, nfds); 394 | 395 | link_head = calloc(nfds, sizeof(void *)); 396 | if (link_head == NULL) 397 | pr_exit("%s: calloc failed", __func__); 398 | 399 | clients = calloc(nfds, sizeof(struct pollfd)); 400 | if (clients == NULL) 401 | pr_exit("%s: calloc failed", __func__); 402 | 403 | for (i = 0; i < nfds; i++) 404 | clients[i].fd = -1; 405 | } 406 | 407 | void ss_exit(void) 408 | { 409 | if (link_head) 410 | free(link_head); 411 | 412 | if (clients) 413 | free(clients); 414 | } 415 | 416 | void poll_events_string(short events, char *events_str) 417 | { 418 | if (events & POLLIN) { 419 | if (strlen(events_str) == 0) 420 | strcat(events_str, "POLLIN"); 421 | else 422 | strcat(events_str, " POLLIN"); 423 | } 424 | 425 | if (events & POLLOUT) { 426 | if (strlen(events_str) == 0) 427 | strcat(events_str, "POLLOUT"); 428 | else 429 | strcat(events_str, " POLLOUT"); 430 | } 431 | } 432 | 433 | int poll_set(int sockfd, short events) 434 | { 435 | char events_str[42] = {'\0'}; 436 | 437 | if (sockfd < 0 || sockfd >= nfds) { 438 | sock_err(sockfd, "%s: illegal sockfd(%d)", __func__, sockfd); 439 | return -1; 440 | } 441 | 442 | clients[sockfd].fd = sockfd; 443 | clients[sockfd].events = events; 444 | poll_events_string(events, events_str); 445 | sock_info(sockfd, "%s: %s", __func__, events_str); 446 | 447 | return 0; 448 | } 449 | 450 | int poll_add(int sockfd, short events) 451 | { 452 | char events_str[42] = {'\0'}; 453 | 454 | if (sockfd < 0 || sockfd >= nfds) { 455 | sock_err(sockfd, "%s: illegal sockfd(%d)", __func__, sockfd); 456 | return -1; 457 | } 458 | 459 | if (clients[sockfd].fd != sockfd) { 460 | sock_warn(sockfd, "%s: sockfd(%d) not in poll", 461 | __func__, sockfd); 462 | return -1; 463 | } 464 | 465 | clients[sockfd].events |= events; 466 | poll_events_string(events, events_str); 467 | sock_info(sockfd, "%s: %s", __func__, events_str); 468 | 469 | return 0; 470 | } 471 | 472 | int poll_rm(int sockfd, short events) 473 | { 474 | char events_str[42] = {'\0'}; 475 | 476 | if (sockfd < 0 || sockfd >= nfds) { 477 | sock_err(sockfd, "%s: illegal sockfd(%d)", __func__, sockfd); 478 | return -1; 479 | } 480 | 481 | clients[sockfd].events &= ~events; 482 | poll_events_string(events, events_str); 483 | sock_info(sockfd, "%s: %s", __func__, events_str); 484 | 485 | return 0; 486 | } 487 | 488 | int poll_del(int sockfd) 489 | { 490 | if (sockfd < 0 || sockfd >= nfds) { 491 | sock_err(sockfd, "%s: illegal sockfd(%d)", __func__, sockfd); 492 | return -1; 493 | } 494 | 495 | clients[sockfd].fd = -1; 496 | sock_info(sockfd, "%s: deleted from poll", __func__); 497 | 498 | return 0; 499 | } 500 | 501 | /** 502 | * time_out - check if it's timed out 503 | * 504 | * @this: the time_t we want to compare(usually is NOW) 505 | * @that: the time_t we want to check 506 | * @value: how long we think it's a timeout 507 | * 508 | * Return: 0 means time out, -1 means not time out 509 | */ 510 | static int time_out(time_t this, time_t that, double value) 511 | { 512 | if (difftime(this, that) > value) 513 | return 0; 514 | else 515 | return -1; 516 | } 517 | 518 | void reaper(void) 519 | { 520 | int sockfd; 521 | double value; 522 | struct link *ln; 523 | time_t now = time(NULL); 524 | static time_t checked = (time_t)-1; 525 | 526 | if (checked == (time_t)-1) { 527 | checked = now; 528 | } else if (time_out(now, checked, TCP_INACTIVE_TIMEOUT) == -1) { 529 | return; 530 | } else { 531 | checked = now; 532 | } 533 | 534 | for (sockfd = 0; sockfd < nfds; sockfd++) { 535 | ln = link_head[sockfd]; 536 | if (ln == NULL) 537 | continue; 538 | 539 | if (ln->state & SERVER) 540 | value = TCP_INACTIVE_TIMEOUT; 541 | else 542 | value = TCP_CONNECT_TIMEOUT; 543 | 544 | if (time_out(now, ln->time, value) == 0) { 545 | if (value == TCP_CONNECT_TIMEOUT) 546 | pr_debug("%s: connect timeout, close\n", 547 | __func__); 548 | 549 | if (value == TCP_INACTIVE_TIMEOUT) 550 | pr_debug("%s: inactive timeout, close\n", 551 | __func__); 552 | 553 | destroy_link(sockfd); 554 | } 555 | } 556 | } 557 | 558 | struct link *create_link(int sockfd, const char *type) 559 | { 560 | struct link *ln; 561 | 562 | ln = calloc(1, sizeof(*ln)); 563 | if (ln == NULL) 564 | goto err; 565 | 566 | ln->text = malloc(TEXT_BUF_SIZE); 567 | if (ln->text == NULL) 568 | goto err; 569 | 570 | ln->cipher = malloc(CIPHER_BUF_SIZE); 571 | if (ln->cipher == NULL) 572 | goto err; 573 | 574 | /* cipher to encrypt local data */ 575 | ln->local_ctx = EVP_CIPHER_CTX_new(); 576 | if (ln->local_ctx == NULL) 577 | goto err; 578 | 579 | /* cipher to decrypt server data */ 580 | ln->server_ctx = EVP_CIPHER_CTX_new(); 581 | if (ln->server_ctx == NULL) 582 | goto err; 583 | 584 | ln->state |= LOCAL; 585 | 586 | ln->local_sockfd = sockfd; 587 | ln->server_sockfd = -1; 588 | ln->time = time(NULL); 589 | 590 | if (link_head[sockfd] != NULL) { 591 | sock_warn(sockfd, "%s: link already exist for sockfd %d", 592 | __func__, sockfd); 593 | goto err; 594 | } 595 | 596 | link_head[sockfd] = ln; 597 | 598 | return ln; 599 | err: 600 | if (ln->server_ctx) 601 | EVP_CIPHER_CTX_free(ln->server_ctx); 602 | 603 | if (ln->local_ctx) 604 | EVP_CIPHER_CTX_free(ln->local_ctx); 605 | 606 | if (ln->text) 607 | free(ln->text); 608 | 609 | if (ln->cipher) 610 | free(ln->cipher); 611 | 612 | if (ln) 613 | free(ln); 614 | 615 | sock_warn(sockfd, "%s: failed", __func__); 616 | return NULL; 617 | } 618 | 619 | struct link *get_link(int sockfd) 620 | { 621 | if (sockfd < 0 || sockfd >= nfds) { 622 | pr_warn("%s: invalid sockfd %d", __func__, sockfd); 623 | return NULL; 624 | } 625 | 626 | if (link_head[sockfd] == NULL) { 627 | sock_warn(sockfd, "%s: link doesn't exist", __func__); 628 | return NULL; 629 | } 630 | 631 | return link_head[sockfd]; 632 | } 633 | 634 | static void free_link(struct link *ln) 635 | { 636 | if (ln->text) 637 | free(ln->text); 638 | 639 | if (ln->cipher) 640 | free(ln->cipher); 641 | 642 | if (ln->local_ctx) 643 | EVP_CIPHER_CTX_free(ln->local_ctx); 644 | 645 | if (ln->server_ctx) 646 | EVP_CIPHER_CTX_free(ln->server_ctx); 647 | 648 | if (ln) 649 | free(ln); 650 | } 651 | 652 | void destroy_link(int sockfd) 653 | { 654 | struct link *ln; 655 | 656 | ln = get_link(sockfd); 657 | if (ln == NULL) 658 | return; 659 | 660 | link_head[ln->local_sockfd] = NULL; 661 | link_head[ln->server_sockfd] = NULL; 662 | poll_del(ln->local_sockfd); 663 | poll_del(ln->server_sockfd); 664 | 665 | if (ln->local_sockfd >= 0) 666 | close(ln->local_sockfd); 667 | 668 | if (ln->server_sockfd >= 0) 669 | close(ln->server_sockfd); 670 | 671 | free_link(ln); 672 | } 673 | 674 | /* for udp, we just bind it, since udp can't listen */ 675 | int do_listen(struct addrinfo *info, const char *type_str) 676 | { 677 | int sockfd, type; 678 | int opt = 1; 679 | struct addrinfo *lp = info; 680 | 681 | if (strcmp(type_str, "tcp") == 0) 682 | type = SOCK_STREAM; 683 | else if (strcmp(type_str, "udp") == 0) 684 | type = SOCK_DGRAM; 685 | else 686 | pr_exit("%s: unknown socket type\n", __func__); 687 | 688 | while (lp) { 689 | if (lp->ai_socktype == type) { 690 | type |= SOCK_NONBLOCK; 691 | sockfd = socket(lp->ai_family, type, 0); 692 | if (sockfd == -1) 693 | goto err; 694 | 695 | if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) != 0) 696 | goto err; 697 | 698 | if (bind(sockfd, lp->ai_addr, lp->ai_addrlen) == -1) 699 | goto err; 700 | 701 | if (type & SOCK_STREAM) 702 | if (listen(sockfd, SOMAXCONN) == -1) 703 | goto err; 704 | 705 | return sockfd; 706 | } 707 | 708 | lp = lp->ai_next; 709 | } 710 | 711 | err: 712 | err_exit("do_listen"); 713 | } 714 | 715 | int connect_server(int sockfd) 716 | { 717 | int new_sockfd, ret, type; 718 | struct link *ln; 719 | struct addrinfo *ai; 720 | 721 | ln = get_link(sockfd); 722 | if (ln == NULL) 723 | return -1; 724 | 725 | if (ln->server_sockfd != -1) { 726 | pr_warn("%s is called twice on link, " 727 | "return without doing anything\n", 728 | __func__); 729 | return 0; 730 | } 731 | 732 | if (ln->state & SS_UDP) 733 | type = SOCK_DGRAM; 734 | else 735 | type = SOCK_STREAM; 736 | 737 | ai = ln->server; 738 | while (ai) { 739 | if (ai->ai_socktype == type) { 740 | type |= SOCK_NONBLOCK; 741 | new_sockfd = socket(ai->ai_family, type, 0); 742 | if (new_sockfd == -1) 743 | goto err; 744 | 745 | link_head[new_sockfd] = ln; 746 | ln->server_sockfd = new_sockfd; 747 | ln->time = time(NULL); 748 | poll_set(new_sockfd, POLLIN); 749 | ret = connect(new_sockfd, ai->ai_addr, ai->ai_addrlen); 750 | if (ret == -1) { 751 | /* it's ok to return inprogress, will 752 | * handle it later */ 753 | if (errno == EINPROGRESS) { 754 | poll_add(new_sockfd, POLLOUT); 755 | return 0; 756 | } else { 757 | goto err; 758 | } 759 | } 760 | 761 | /* sucessfully connected */ 762 | ln->state |= SERVER; 763 | sock_info(new_sockfd, "%s: connected", __func__); 764 | return 0; 765 | } 766 | 767 | ai = ai->ai_next; 768 | } 769 | 770 | err: 771 | perror("connect_server"); 772 | return -1; 773 | } 774 | 775 | int add_data(int sockfd, struct link *ln, 776 | const char *type, char *data, int size) 777 | { 778 | char *buf; 779 | int len; 780 | 781 | if (strcmp(type, "text") == 0) { 782 | buf = ln->text; 783 | len = ln->text_len; 784 | 785 | if (len + size > TEXT_BUF_SIZE) { 786 | sock_warn(sockfd, "%s: data exceed max length(%d/%d)", 787 | __func__, len + size, TEXT_BUF_SIZE); 788 | return -1; 789 | } 790 | 791 | ln->text_len += size; 792 | } else if (strcmp(type, "cipher") == 0) { 793 | buf = ln->cipher; 794 | len = ln->cipher_len; 795 | 796 | if (len + size > CIPHER_BUF_SIZE) { 797 | sock_warn(sockfd, "%s: data exceed max length(%d/%d)", 798 | __func__, len + size, CIPHER_BUF_SIZE); 799 | return -1; 800 | } 801 | 802 | ln->cipher_len += size; 803 | } else { 804 | sock_warn(sockfd, "%s: unknown type", __func__); 805 | return -1; 806 | } 807 | 808 | /* if len == 0, no data need to be moved */ 809 | if (len > 0) 810 | memmove(buf + size, buf, len); 811 | 812 | memcpy(buf, data, size); 813 | return 0; 814 | } 815 | 816 | int rm_data(int sockfd, struct link *ln, const char *type, int size) 817 | { 818 | char *buf; 819 | int len; 820 | 821 | if (strcmp(type, "text") == 0) { 822 | buf = ln->text; 823 | 824 | if (ln->text_len < size) { 825 | sock_warn(sockfd, "%s: size is too big(%d/%d)", 826 | __func__, size, ln->text_len); 827 | return -1; 828 | } 829 | 830 | ln->text_len -= size; 831 | len = ln->text_len; 832 | } else if (strcmp(type, "cipher") == 0) { 833 | buf = ln->cipher; 834 | 835 | if (ln->cipher_len < size) { 836 | sock_warn(sockfd, "%s: size is too big(%d/%d)", 837 | __func__, size, ln->cipher_len); 838 | return -1; 839 | } 840 | 841 | ln->cipher_len -= size; 842 | len = ln->cipher_len; 843 | } else { 844 | sock_warn(sockfd, "%s: unknown type", __func__); 845 | return -1; 846 | } 847 | 848 | memmove(buf, buf + size, len); 849 | 850 | return 0; 851 | } 852 | 853 | int check_ss_header(int sockfd, struct link *ln) 854 | { 855 | int ret; 856 | char atyp; 857 | char addr[256]; 858 | unsigned short port; 859 | char port_str[6]; 860 | short addr_len; 861 | struct ss_header *req; 862 | struct addrinfo hint; 863 | struct addrinfo *res; 864 | 865 | memset(&hint, 0, sizeof(hint)); 866 | hint.ai_socktype = SOCK_STREAM; 867 | 868 | req = (void *)ln->text; 869 | 870 | if (ln->state & SS_UDP) { 871 | hint.ai_socktype = SOCK_DGRAM; 872 | } else { 873 | hint.ai_socktype = SOCK_STREAM; 874 | } 875 | 876 | atyp = req->atyp; 877 | if (atyp == SOCKS5_ADDR_IPV4) { 878 | addr_len = 4; 879 | 880 | /* atyp(1) + ipv4_addrlen(4) + port(2) */ 881 | if (ln->text_len < 7) { 882 | goto too_short; 883 | } 884 | 885 | hint.ai_family = AF_INET; 886 | 887 | if (inet_ntop(AF_INET, req->dst, addr, sizeof(addr)) == NULL) { 888 | sock_warn(sockfd, "%s: inet_ntop() %s", 889 | __func__, strerror(errno)); 890 | return -1; 891 | } 892 | 893 | port = ntohs(*(unsigned short *)(req->dst + addr_len)); 894 | } else if (atyp == SOCKS5_ADDR_DOMAIN) { 895 | addr_len = req->dst[0]; 896 | 897 | /* atyp(1) + addr_size(1) + domain_len(addr_len) + port(2) */ 898 | if (ln->text_len < 1 + 1 + addr_len + 2) 899 | goto too_short; 900 | 901 | hint.ai_family = AF_UNSPEC; 902 | strncpy(addr, req->dst + 1, addr_len); 903 | addr[addr_len] = '\0'; 904 | port = ntohs(*(unsigned short *)(req->dst + addr_len + 1)); 905 | /* to compute the right data length(except header) */ 906 | addr_len += 1; 907 | } else if (atyp == SOCKS5_ADDR_IPV6) { 908 | hint.ai_family = AF_INET6; 909 | addr_len = 16; 910 | 911 | if (inet_ntop(AF_INET6, req->dst, addr, sizeof(addr)) == NULL) { 912 | sock_warn(sockfd, "%s: inet_ntop() %s", 913 | __func__, strerror(errno)); 914 | return -1; 915 | } 916 | 917 | port = ntohs(*(unsigned short *)(req->dst + addr_len)); 918 | } else { 919 | sock_warn(sockfd, "%s: ATYP(%d) isn't legal"); 920 | return -1; 921 | } 922 | 923 | sock_info(sockfd, "%s: remote address: %s; port: %d", 924 | __func__, addr, port); 925 | sprintf(port_str, "%d", port); 926 | ret = getaddrinfo(addr, port_str, &hint, &res); 927 | if (ret != 0) { 928 | sock_warn(sockfd, "getaddrinfo error: %s", gai_strerror(ret)); 929 | return -1; 930 | } 931 | 932 | if (ln->state & SS_UDP) { 933 | ln->ss_header_len = ln->text_len; 934 | } else { 935 | ln->ss_header_len = 1 + addr_len + 2; 936 | if (rm_data(sockfd, ln, "text", ln->ss_header_len) == -1) 937 | return -1; 938 | } 939 | 940 | ln->server = res; 941 | 942 | if (connect_server(sockfd) == -1) 943 | return -1; 944 | 945 | return 0; 946 | 947 | too_short: 948 | sock_warn(sockfd, "%s: text is too short", 949 | __func__); 950 | return -1; 951 | } 952 | 953 | int check_socks5_auth_header(int sockfd, struct link *ln) 954 | { 955 | unsigned short i; 956 | struct socks5_auth_request *req; 957 | 958 | if (ln->text_len < 3) { 959 | sock_warn(sockfd, "%s: text len is smaller than auth request", 960 | __func__); 961 | return -1; 962 | } 963 | 964 | req = (void *)ln->text; 965 | 966 | if (req->ver != 0x05) { 967 | sock_warn(sockfd, "%s: VER(%d) is not 5", 968 | __func__, req->ver); 969 | return -1; 970 | } 971 | 972 | i = req->nmethods; 973 | if ((i + 2) != ln->text_len) { 974 | sock_warn(sockfd, "%s: NMETHODS(%d) isn't correct", 975 | __func__, i); 976 | return -1; 977 | } 978 | 979 | while (i-- > 0) 980 | if (req->methods[i] == 0x00) 981 | return 0; 982 | 983 | sock_warn(sockfd, "%s: only support NO AUTHENTICATION"); 984 | return -1; 985 | } 986 | 987 | int check_socks5_cmd_header(int sockfd, struct link *ln) 988 | { 989 | char cmd, atyp; 990 | int ss_header_len; 991 | struct socks5_cmd_request *req; 992 | 993 | req = (void *)ln->text; 994 | 995 | if (req->ver != 0x05) { 996 | sock_warn(sockfd, "%s: VER(%d) is not 5", 997 | __func__, req->ver); 998 | return -1; 999 | } 1000 | 1001 | cmd = req->cmd; 1002 | if (cmd == SOCKS5_CONNECT) { 1003 | /* nothing to do */ 1004 | } else if (cmd == SOCKS5_UDP_ASSOCIATE) { 1005 | ln->state |= SS_UDP; 1006 | sock_info(sockfd, "%s: udp associate received", 1007 | __func__); 1008 | sock_warn(sockfd, "udp socks5 not supported(for now)"); 1009 | return -1; 1010 | } else { 1011 | sock_warn(sockfd, "%s: CMD(%d) isn't supported", cmd); 1012 | return -1; 1013 | } 1014 | 1015 | if (req->rsv != 0x00) { 1016 | sock_warn(sockfd, "%s: RSV(%d) is not 0x00"); 1017 | return -1; 1018 | } 1019 | 1020 | atyp = req->atyp; 1021 | /* the following magic number 3 is actually ver(1) + cmd(1) + 1022 | * rsv(1) */ 1023 | if (atyp == SOCKS5_ADDR_IPV4) { 1024 | /* atyp(1) + ipv4(4) + port(2) */ 1025 | ss_header_len = 1 + 4 + 2; 1026 | 1027 | if (ln->text_len < ss_header_len + 3) 1028 | goto too_short; 1029 | } else if (atyp == SOCKS5_ADDR_DOMAIN) { 1030 | /* atyp(1) + addr_size(1) + domain_length(req->dst[0]) + 1031 | * port(2) */ 1032 | ss_header_len = 1 + 1 + req->dst[0] + 2; 1033 | 1034 | if (ln->text_len < ss_header_len + 3) 1035 | goto too_short; 1036 | } else if (atyp == SOCKS5_ADDR_IPV6) { 1037 | /* atyp(1) + ipv6_addrlen(16) + port(2) */ 1038 | ss_header_len = 1 + 16 + 2; 1039 | 1040 | if (ln->text_len < ss_header_len + 3) 1041 | goto too_short; 1042 | } else { 1043 | sock_warn(sockfd, "%s: ATYP(%d) isn't legal"); 1044 | return -1; 1045 | } 1046 | 1047 | ln->ss_header_len = ss_header_len; 1048 | 1049 | /* remove VER, CMD, RSV for shadowsocks protocol */ 1050 | if (rm_data(sockfd, ln, "text", 3) == -1) 1051 | return -1; 1052 | 1053 | /* copy ss tcp header to cipher buffer, it will be sent 1054 | * together with data received from local */ 1055 | memcpy(ln->cipher, ln->text, ln->ss_header_len); 1056 | 1057 | /* all seem okay, connect to server! */ 1058 | if (connect_server(sockfd) == -1) 1059 | return -1; 1060 | 1061 | return 0; 1062 | 1063 | too_short: 1064 | sock_warn(sockfd, "%s: text is too short", 1065 | __func__); 1066 | return -1; 1067 | } 1068 | 1069 | int create_socks5_auth_reply(int sockfd, struct link *ln, bool ok) 1070 | { 1071 | struct socks5_auth_reply rep; 1072 | 1073 | rep.ver = 0x05; 1074 | 1075 | if (ok) 1076 | rep.method = SOCKS5_METHOD_NOT_REQUIRED; 1077 | else 1078 | rep.method = SOCKS5_METHOD_ERROR; 1079 | 1080 | ln->text_len = 0; 1081 | 1082 | if (add_data(sockfd, ln, "text", (void *)&rep, sizeof(rep)) == -1) 1083 | return -1; 1084 | 1085 | return 0; 1086 | } 1087 | 1088 | int create_socks5_cmd_reply(int sockfd, struct link *ln, int cmd) 1089 | { 1090 | unsigned short port; 1091 | void *addrptr; 1092 | int addr_len; 1093 | struct sockaddr_storage ss_addr; 1094 | int len = sizeof(struct sockaddr_storage); 1095 | struct addrinfo *ai = ln->server; 1096 | struct socks5_cmd_reply *rep = (void *)ln->text; 1097 | 1098 | rep->ver = 0x05; 1099 | rep->rep = cmd; 1100 | rep->rsv = 0x00; 1101 | 1102 | if (getpeername(sockfd, (struct sockaddr *)&ss_addr, 1103 | (void *)&len) == -1) { 1104 | sock_warn(sockfd, "%s: getsockname() %s", 1105 | __func__, strerror(errno)); 1106 | return -1; 1107 | } 1108 | 1109 | while (ai) { 1110 | if (ai->ai_family == ss_addr.ss_family) { 1111 | if (ai->ai_family == AF_INET) { 1112 | rep->atyp = SOCKS5_ADDR_IPV4; 1113 | port = ((SA_IN *)ai->ai_addr)->sin_port; 1114 | addrptr = &((SA_IN *)ai->ai_addr)->sin_addr; 1115 | addr_len = sizeof(struct in_addr); 1116 | } else { 1117 | rep->atyp = SOCKS5_ADDR_IPV6; 1118 | port = ((SA_IN6 *)ai->ai_addr)->sin6_port; 1119 | addrptr = &((SA_IN6 *)ai->ai_addr)->sin6_addr; 1120 | addr_len = sizeof(struct in6_addr); 1121 | } 1122 | 1123 | break; 1124 | } 1125 | 1126 | ai = ai->ai_next; 1127 | } 1128 | 1129 | if (ai == NULL) 1130 | return -1; 1131 | 1132 | memcpy(rep->bnd, addrptr, addr_len); 1133 | memcpy(rep->bnd + addr_len, (void *)&port, sizeof(short)); 1134 | 1135 | len = sizeof(*rep) + addr_len + 2; 1136 | ln->text_len = 0; 1137 | if (add_data(sockfd, ln, "text", (void *)rep, len) == -1) 1138 | return -1; 1139 | 1140 | return 0; 1141 | } 1142 | 1143 | int do_read(int sockfd, struct link *ln, const char *type, int offset) 1144 | { 1145 | int ret, len; 1146 | char *buf; 1147 | 1148 | if (strcmp(type, "text") == 0) { 1149 | buf = ln->text + offset; 1150 | len = TEXT_BUF_SIZE - offset; 1151 | } else if (strcmp(type, "cipher") == 0) { 1152 | buf = ln->cipher + offset; 1153 | /* cipher read only accept text buffer length data, or 1154 | * it may overflow text buffer */ 1155 | len = TEXT_BUF_SIZE - offset; 1156 | } else { 1157 | sock_warn(sockfd, "%s: unknown type %s", 1158 | __func__, type); 1159 | return -2; 1160 | } 1161 | 1162 | ret = recv(sockfd, buf, len, 0); 1163 | if (ret == -1) { 1164 | if (errno != EAGAIN && errno != EWOULDBLOCK) { 1165 | sock_info(sockfd, "%s(%s): recv() %s", 1166 | __func__, type, strerror(errno)); 1167 | return -2; 1168 | } 1169 | 1170 | poll_add(sockfd, POLLIN); 1171 | return -1; 1172 | } else if (ret == 0) { 1173 | /* recv() returned 0 means the peer has shut down, 1174 | * return -2 to let the caller do the closing work */ 1175 | sock_debug(sockfd, "%s(%s): the peer has shut down", 1176 | __func__, type); 1177 | return -2; 1178 | } 1179 | 1180 | if (strcmp(type, "text") == 0) { 1181 | ln->text_len = ret + offset; 1182 | } else if (strcmp(type, "cipher") == 0) { 1183 | ln->cipher_len = ret + offset; 1184 | } 1185 | 1186 | ln->time = time(NULL); 1187 | sock_debug(sockfd, "%s(%s): recv(%d), offset(%d)", 1188 | __func__, type, ret, offset); 1189 | pr_link_debug(ln); 1190 | 1191 | return ret; 1192 | } 1193 | 1194 | int do_send(int sockfd, struct link *ln, const char *type, int offset) 1195 | { 1196 | int ret, len; 1197 | char *buf; 1198 | 1199 | if (strcmp(type, "text") == 0) { 1200 | buf = ln->text + offset; 1201 | len = ln->text_len - offset; 1202 | } else if (strcmp(type, "cipher") == 0) { 1203 | buf = ln->cipher + offset; 1204 | len = ln->cipher_len - offset; 1205 | } else { 1206 | sock_warn(sockfd, "%s: unknown type %s", 1207 | __func__, type); 1208 | return -2; 1209 | } 1210 | 1211 | ret = send(sockfd, buf, len, 0); 1212 | if (ret == -1) { 1213 | if (errno != EAGAIN && errno != EWOULDBLOCK && 1214 | errno != ENOTCONN && errno != EPIPE) { 1215 | sock_warn(sockfd, "%s(%s): send() %s", 1216 | __func__, type, strerror(errno)); 1217 | return -2; 1218 | } else { 1219 | /* wait for unblocking send, or wait for 1220 | * connection finished */ 1221 | poll_add(sockfd, POLLOUT); 1222 | return -1; 1223 | } 1224 | } 1225 | 1226 | if (rm_data(sockfd, ln, type, ret) == -1) 1227 | return -2; 1228 | 1229 | ln->time = time(NULL); 1230 | 1231 | if (ret != len) { 1232 | poll_add(sockfd, POLLOUT); 1233 | sock_debug(sockfd, "%s(%s): send() partial send(%d/%d)", 1234 | __func__, type, ret, len); 1235 | return -1; 1236 | } 1237 | 1238 | sock_debug(sockfd, "%s(%s): send(%d), offset(%d)", 1239 | __func__, type, ret, offset); 1240 | pr_link_debug(ln); 1241 | 1242 | return ret; 1243 | } 1244 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Zhao, Gang 3 | * This is free software; you can redistribute it and/or modify 4 | * it under the terms of the MIT license. See COPYING for details. 5 | */ 6 | 7 | #ifndef SS_COMMON_H 8 | #define SS_COMMON_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "log.h" 19 | 20 | #define SA struct sockaddr 21 | #define SA_IN struct sockaddr_in 22 | #define SA_IN6 struct sockaddr_in6 23 | #define SS struct sockaddr_storage 24 | 25 | #define TCP_INACTIVE_TIMEOUT 120 26 | #define TCP_CONNECT_TIMEOUT 15 27 | #define DEFAULT_MAX_CONNECTION 1024 28 | #define TEXT_BUF_SIZE (1024 * 8) 29 | #define CIPHER_BUF_SIZE (TEXT_BUF_SIZE + EVP_MAX_BLOCK_LENGTH + \ 30 | EVP_MAX_IV_LENGTH) 31 | #define MAX_DOMAIN_LEN 255 32 | #define MAX_PORT_STRING_LEN 5 33 | #define MAX_PWD_LEN 16 34 | #define MAX_METHOD_NAME_LEN 16 35 | 36 | struct ss_option { 37 | char server_addr[MAX_DOMAIN_LEN + 1]; 38 | char local_addr[MAX_DOMAIN_LEN + 1]; 39 | char server_port[MAX_PORT_STRING_LEN + 1]; 40 | char local_port[MAX_PORT_STRING_LEN + 1]; 41 | char password[MAX_PWD_LEN + 1]; 42 | char method[MAX_METHOD_NAME_LEN + 1]; 43 | bool daemon; 44 | }; 45 | 46 | #define BITS(x) (1 << (x)) 47 | 48 | enum link_state { 49 | LOCAL = BITS(1), 50 | SERVER = BITS(2), 51 | LOCAL_READ_PENDING = BITS(3), 52 | LOCAL_SEND_PENDING = BITS(4), 53 | SERVER_READ_PENDING = BITS(5), 54 | SERVER_SEND_PENDING = BITS(6), 55 | SOCKS5_AUTH_REQUEST_RECEIVED = BITS(7), 56 | SOCKS5_AUTH_REPLY_SENT = BITS(8), 57 | SOCKS5_CMD_REQUEST_RECEIVED = BITS(9), 58 | SOCKS5_CMD_REPLY_SENT = BITS(10), 59 | SS_TCP_HEADER_SENT = BITS(11), 60 | SS_TCP_HEADER_RECEIVED = BITS(12), 61 | SS_IV_SENT = BITS(13), 62 | SS_IV_RECEIVED = BITS(14), 63 | SS_UDP = BITS(15), 64 | }; 65 | 66 | #define LINKED (LOCAL | SERVER) 67 | #define LOCAL_PENDING (LOCAL_READ_PENDING | LOCAL_SEND_PENDING) 68 | #define SERVER_PENDING (SERVER_READ_PENDING | SERVER_SEND_PENDING) 69 | 70 | struct link { 71 | enum link_state state; 72 | time_t time; 73 | int local_sockfd; 74 | int server_sockfd; 75 | int text_len; 76 | int cipher_len; 77 | int ss_header_len; 78 | EVP_CIPHER_CTX *local_ctx; 79 | EVP_CIPHER_CTX *server_ctx; 80 | struct addrinfo *server; 81 | void *text; 82 | void *cipher; 83 | char local_iv[EVP_MAX_IV_LENGTH]; 84 | char server_iv[EVP_MAX_IV_LENGTH]; 85 | }; 86 | 87 | #define SOCKS5_METHOD_NOT_REQUIRED 0x00 88 | #define SOCKS5_METHOD_ERROR 0XFF 89 | 90 | #define SOCKS5_CONNECT 0x01 91 | #define SOCKS5_UDP_ASSOCIATE 0x03 92 | 93 | #define SOCKS5_ADDR_IPV4 0X01 94 | #define SOCKS5_ADDR_DOMAIN 0X03 95 | #define SOCKS5_ADDR_IPV6 0X04 96 | 97 | #define SOCKS5_CMD_REP_SUCCEEDED 0x00 98 | #define SOCKS5_CMD_REP_FAILED 0x11 99 | 100 | struct socks5_auth_request { 101 | char ver; 102 | char nmethods; 103 | char methods[]; 104 | }; 105 | 106 | struct socks5_auth_reply { 107 | char ver; 108 | char method; 109 | }; 110 | 111 | struct socks5_cmd_request { 112 | char ver; 113 | char cmd; 114 | char rsv; 115 | char atyp; 116 | char dst[]; 117 | }; 118 | 119 | struct socks5_cmd_reply { 120 | char ver; 121 | char rep; 122 | char rsv; 123 | char atyp; 124 | char bnd[]; 125 | }; 126 | 127 | struct socks5_udp_header { 128 | char rsv[2]; 129 | char frag; 130 | char atyp; 131 | char dst[]; 132 | }; 133 | 134 | struct ss_header { 135 | char atyp; 136 | char dst[]; 137 | }; 138 | 139 | extern int nfds; 140 | extern struct pollfd *clients; 141 | extern struct ss_option ss_opt; 142 | extern struct link **link_head; 143 | 144 | void check_ss_option(int argc, char **argv, const char *type); 145 | void pr_data(FILE *fp, const char *name, char *data, int len); 146 | void pr_link_debug(struct link *ln); 147 | void pr_link_info(struct link *ln); 148 | void pr_link_warn(struct link *ln); 149 | void ss_init(void); 150 | void ss_exit(void); 151 | int poll_set(int sockfd, short events); 152 | int poll_add(int sockfd, short events); 153 | int poll_rm(int sockfd, short events); 154 | int poll_del(int sockfd); 155 | void reaper(void); 156 | struct link *create_link(int sockfd, const char *type); 157 | struct link *get_link(int sockfd); 158 | void destroy_link(int sockfd); 159 | int do_listen(struct addrinfo *info, const char *type); 160 | int connect_server(int sockfd); 161 | int add_data(int sockfd, struct link *ln, 162 | const char *type, char *data, int size); 163 | int rm_data(int sockfd, struct link *ln, const char *type, int size); 164 | int check_ss_header(int sockfd, struct link *ln); 165 | int check_socks5_auth_header(int sockfd, struct link *ln); 166 | int check_socks5_cmd_header(int sockfd, struct link *ln); 167 | int create_socks5_auth_reply(int sockfd, struct link *ln, bool ok); 168 | int create_socks5_cmd_reply(int sockfd, struct link *ln, int cmd); 169 | int do_read(int sockfd, struct link *ln, const char *type, int offset); 170 | int do_send(int sockfd, struct link *ln, const char *type, int offset); 171 | 172 | #endif 173 | -------------------------------------------------------------------------------- /crypto.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Zhao, Gang 3 | * This is free software; you can redistribute it and/or modify 4 | * it under the terms of the MIT license. See COPYING for details. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "common.h" 16 | #include "crypto.h" 17 | 18 | int iv_len; 19 | static const EVP_CIPHER *evp_cipher; 20 | static const EVP_MD *md; 21 | static char key[EVP_MAX_KEY_LENGTH]; 22 | static int key_len; 23 | 24 | static const char supported_method[][MAX_METHOD_NAME_LEN] = { 25 | "aes-128-cfb", 26 | "aes-192-cfb", 27 | "aes-256-cfb", 28 | "bf-cfb", 29 | /* "camellia-128-cfb", */ 30 | /* "camellia-192-cfb", */ 31 | /* "camellia-256-cfb", */ 32 | "cast5-cfb", 33 | "des-cfb", 34 | /* "idea-cfb", */ 35 | "rc2-cfb", 36 | "rc4", 37 | "seed-cfb", 38 | /* "salsa20-ctr", */ 39 | }; 40 | 41 | int get_method(char *password, char *method) 42 | { 43 | int ret; 44 | 45 | md = EVP_get_digestbyname("MD5"); 46 | if (md == NULL) 47 | goto err; 48 | 49 | evp_cipher = EVP_get_cipherbyname(ss_opt.method); 50 | if (evp_cipher == NULL) 51 | goto err; 52 | 53 | key_len = EVP_CIPHER_key_length(evp_cipher); 54 | iv_len = EVP_CIPHER_iv_length(evp_cipher); 55 | 56 | ret = EVP_BytesToKey(evp_cipher, md, NULL, 57 | (void *)password, strlen(password), 1, 58 | (void *)key, NULL); 59 | if (ret == 0) 60 | goto err; 61 | 62 | key[key_len] = '\0'; 63 | /* pr_data(stdout, "password", password, strlen(password)); */ 64 | /* pr_data(stdout, "key", key, key_len); */ 65 | 66 | return 0; 67 | err: 68 | ERR_print_errors_fp(stderr); 69 | pr_exit("%s: failed\n", __func__); 70 | } 71 | 72 | int crypto_init(char *password, char *method) 73 | { 74 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 75 | ERR_load_crypto_strings(); 76 | OpenSSL_add_all_algorithms(); 77 | OPENSSL_config(NULL); 78 | #endif 79 | 80 | if (get_method(password, method) == -1) 81 | return -1; 82 | 83 | return 0; 84 | } 85 | 86 | void crypto_exit(void) 87 | { 88 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 89 | EVP_cleanup(); 90 | ERR_free_strings(); 91 | #endif 92 | } 93 | 94 | int add_iv(int sockfd, struct link *ln) 95 | { 96 | int ret; 97 | char *iv_p; 98 | 99 | if (sockfd == ln->local_sockfd) 100 | iv_p = ln->local_iv; 101 | else if (sockfd == ln->server_sockfd) 102 | iv_p = ln->server_iv; 103 | else 104 | goto err; 105 | 106 | ret = add_data(sockfd, ln, "cipher", iv_p, iv_len); 107 | if (ret != 0) 108 | goto err; 109 | 110 | ln->state |= SS_IV_SENT; 111 | 112 | return 0; 113 | err: 114 | sock_warn(sockfd, "%s failed", __func__); 115 | return -1; 116 | } 117 | 118 | /* iv is in the first iv_len byptes of ss tcp/udp header */ 119 | int receive_iv(int sockfd, struct link *ln) 120 | { 121 | int ret; 122 | char *iv_p; 123 | 124 | if (sockfd == ln->local_sockfd) 125 | iv_p = ln->local_iv; 126 | else if (sockfd == ln->server_sockfd) 127 | iv_p = ln->server_iv; 128 | else 129 | goto err; 130 | 131 | memcpy(iv_p, ln->cipher, iv_len); 132 | ret = rm_data(sockfd, ln, "cipher", iv_len); 133 | if (ret != 0) 134 | goto err; 135 | 136 | ln->state |= SS_IV_RECEIVED; 137 | 138 | return 0; 139 | err: 140 | sock_warn(sockfd, "%s failed", __func__); 141 | return -1; 142 | } 143 | 144 | static int check_cipher(int sockfd, struct link *ln, const char *type) 145 | { 146 | int ret; 147 | char *iv_p; 148 | EVP_CIPHER_CTX *ctx_p; 149 | 150 | if (sockfd == ln->local_sockfd) { 151 | iv_p = ln->local_iv; 152 | ctx_p = ln->local_ctx; 153 | } else if (sockfd == ln->server_sockfd) { 154 | iv_p = ln->server_iv; 155 | ctx_p = ln->server_ctx; 156 | } else { 157 | goto err; 158 | } 159 | 160 | if (strcmp(type, "encrypt") == 0 && 161 | !(ln->state & SS_IV_SENT)) { 162 | if (RAND_bytes((void *)iv_p, iv_len) == -1) 163 | goto err; 164 | 165 | iv_p[iv_len] = '\0'; 166 | 167 | ret = EVP_EncryptInit_ex(ctx_p, evp_cipher, 168 | NULL, (void *)key, 169 | (void *)iv_p); 170 | 171 | if (ret != 1) 172 | goto err; 173 | } else if (strcmp(type, "decrypt") == 0 && 174 | !(ln->state & SS_IV_RECEIVED)) { 175 | if (receive_iv(sockfd, ln) == -1) 176 | goto err; 177 | 178 | ret = EVP_DecryptInit_ex(ctx_p, evp_cipher, 179 | NULL, (void *)key, 180 | (void *)iv_p); 181 | 182 | if (ret != 1) 183 | goto err; 184 | } 185 | 186 | return 0; 187 | err: 188 | sock_warn(sockfd, "%s failed", __func__); 189 | return -1; 190 | } 191 | 192 | int crypto_encrypt(int sockfd, struct link *ln) 193 | { 194 | int len, cipher_len; 195 | EVP_CIPHER_CTX *ctx_p; 196 | 197 | if (check_cipher(sockfd, ln, "encrypt") == -1) 198 | goto err; 199 | 200 | if (sockfd == ln->local_sockfd) { 201 | ctx_p = ln->local_ctx; 202 | } else if (sockfd == ln->server_sockfd) { 203 | ctx_p = ln->server_ctx; 204 | } else { 205 | goto err; 206 | } 207 | 208 | if (EVP_EncryptUpdate(ctx_p, ln->cipher, &len, 209 | ln->text, ln->text_len) != 1) 210 | goto err; 211 | 212 | cipher_len = len; 213 | ln->cipher_len = cipher_len; 214 | 215 | if (!(ln->state & SS_IV_SENT)) 216 | if (add_iv(sockfd, ln) == -1) 217 | goto err; 218 | 219 | /* encryption succeeded, so text buffer is not needed */ 220 | ln->text_len = 0; 221 | 222 | return ln->cipher_len; 223 | err: 224 | ERR_print_errors_fp(stderr); 225 | pr_link_warn(ln); 226 | sock_warn(sockfd, "%s failed", __func__); 227 | return -1; 228 | } 229 | 230 | int crypto_decrypt(int sockfd, struct link *ln) 231 | { 232 | int len, text_len; 233 | EVP_CIPHER_CTX *ctx_p; 234 | 235 | if (check_cipher(sockfd, ln, "decrypt") == -1) 236 | goto err; 237 | 238 | if (sockfd == ln->local_sockfd) { 239 | ctx_p = ln->local_ctx; 240 | } else if (sockfd == ln->server_sockfd) { 241 | ctx_p = ln->server_ctx; 242 | } else { 243 | goto err; 244 | } 245 | 246 | if (EVP_DecryptUpdate(ctx_p, ln->text, &len, 247 | ln->cipher, ln->cipher_len) != 1) { 248 | goto err; 249 | } 250 | 251 | text_len = len; 252 | ln->text_len = text_len; 253 | /* decryption succeeded, so cipher buffer is not needed */ 254 | ln->cipher_len = 0; 255 | 256 | return text_len; 257 | err: 258 | ERR_print_errors_fp(stderr); 259 | pr_link_warn(ln); 260 | sock_warn(sockfd, "%s failed\n", __func__); 261 | return -1; 262 | } 263 | -------------------------------------------------------------------------------- /crypto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Zhao, Gang 3 | * This is free software; you can redistribute it and/or modify 4 | * it under the terms of the MIT license. See COPYING for details. 5 | */ 6 | 7 | #ifndef SS_CRYPTO_H 8 | #define SS_CRYPTO_H 9 | 10 | #include 11 | 12 | extern char password[MAX_PWD_LEN + 1]; 13 | extern char method[MAX_METHOD_NAME_LEN + 1]; 14 | extern int iv_len; 15 | 16 | int crypto_init(char *key, char *method); 17 | void crypto_exit(void); 18 | int crypto_encrypt(int sockfd, struct link *ln); 19 | int crypto_decrypt(int sockfd, struct link *ln); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Zhao, Gang 3 | * This is free software; you can redistribute it and/or modify 4 | * it under the terms of the MIT license. See COPYING for details. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "log.h" 19 | 20 | static int _pr_addrinfo(int level, struct addrinfo *info, 21 | const char *fmt, va_list ap) 22 | { 23 | unsigned short port; 24 | int offset; 25 | char log[1024]; 26 | char addr[INET6_ADDRSTRLEN]; 27 | struct addrinfo *ai = info; 28 | void *addrptr; 29 | 30 | vsprintf(log, fmt, ap); 31 | strcat(log, ":"); 32 | offset = strlen(log); 33 | 34 | while (ai) { 35 | if (ai->ai_family == AF_INET) { 36 | addrptr = &((struct sockaddr_in *)ai->ai_addr)->sin_addr; 37 | port = ntohs(((struct sockaddr_in *)ai->ai_addr)->sin_port); 38 | } else { 39 | addrptr = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; 40 | port = ntohs(((struct sockaddr_in6 *)ai->ai_addr)->sin6_port); 41 | } 42 | 43 | if (inet_ntop(ai->ai_family, addrptr, addr, 44 | INET6_ADDRSTRLEN) == NULL) { 45 | return errno; 46 | } 47 | 48 | if (ai->ai_socktype == SOCK_STREAM) 49 | sprintf(log + offset, " %s:%d(tcp)", addr, port); 50 | else if (ai->ai_socktype == SOCK_DGRAM) 51 | sprintf(log + offset, " %s:%d(udp)", addr, port); 52 | ai = ai->ai_next; 53 | } 54 | 55 | strcat(log, "\n"); 56 | syslog(level, "%s", log); 57 | 58 | return 0; 59 | } 60 | 61 | void pr_ai_debug(struct addrinfo *info, const char *fmt, ...) 62 | { 63 | int ret; 64 | va_list ap; 65 | 66 | va_start(ap, fmt); 67 | 68 | ret = _pr_addrinfo(LOG_DEBUG, info, fmt, ap); 69 | if (ret != 0) 70 | pr_warn("%s: %s\n", __func__, strerror(ret)); 71 | 72 | va_end(ap); 73 | } 74 | 75 | void pr_ai_info(struct addrinfo *info, const char *fmt, ...) 76 | { 77 | int ret; 78 | va_list ap; 79 | 80 | va_start(ap, fmt); 81 | 82 | ret = _pr_addrinfo(LOG_INFO, info, fmt, ap); 83 | if (ret != 0) 84 | pr_warn("%s: %s\n", __func__, strerror(ret)); 85 | 86 | va_end(ap); 87 | } 88 | 89 | void pr_ai_notice(struct addrinfo *info, const char *fmt, ...) 90 | { 91 | int ret; 92 | va_list ap; 93 | 94 | va_start(ap, fmt); 95 | 96 | ret = _pr_addrinfo(LOG_NOTICE, info, fmt, ap); 97 | if (ret != 0) 98 | pr_warn("%s: %s\n", __func__, strerror(ret)); 99 | 100 | va_end(ap); 101 | } 102 | 103 | static int get_sock_addr(int sockfd, char *str, int *port, const char *type) 104 | { 105 | void *addrptr; 106 | struct sockaddr_storage ss_addr; 107 | int len = sizeof(struct sockaddr_storage); 108 | 109 | if (strcmp(type, "peer") == 0) { 110 | if (getpeername(sockfd, (struct sockaddr *)&ss_addr, 111 | (void *)&len) == -1) 112 | goto err; 113 | } else if (strcmp(type, "sock") == 0) { 114 | if (getsockname(sockfd, (struct sockaddr *)&ss_addr, 115 | (void *)&len) == -1) 116 | goto err; 117 | } 118 | 119 | if (ss_addr.ss_family == AF_INET) { 120 | addrptr = &((struct sockaddr_in *)&ss_addr)->sin_addr; 121 | *port = ntohs(((struct sockaddr_in *)&ss_addr)->sin_port); 122 | } else if (ss_addr.ss_family == AF_INET6) { 123 | addrptr = &((struct sockaddr_in6 *)&ss_addr)->sin6_addr; 124 | *port = ntohs(((struct sockaddr_in6 *)&ss_addr)->sin6_port); 125 | } else { 126 | pr_exit("%s: unsupported address family\n", __func__); 127 | } 128 | 129 | if (inet_ntop(ss_addr.ss_family, addrptr, str, 130 | INET6_ADDRSTRLEN) == NULL) 131 | goto err; 132 | 133 | return 0; 134 | 135 | err: 136 | return -1; 137 | } 138 | 139 | static void sock_print(int sockfd, int level, const char *fmt, va_list ap) 140 | { 141 | int offset, port; 142 | char *type; 143 | char str[INET6_ADDRSTRLEN] = {'\0'}; 144 | char log[1024]; 145 | 146 | if (get_sock_addr(sockfd, str, &port, "peer") == 0) 147 | type = "peer"; 148 | else if (get_sock_addr(sockfd, str, &port, "sock") == 0) 149 | type = "sock"; 150 | else 151 | type = "sockfd"; 152 | 153 | vsprintf(log, fmt, ap); 154 | offset = strlen(log); 155 | 156 | if (strcmp(type, "peer") == 0) 157 | sprintf(log + offset, " (peer)%s:%d\n", str, port); 158 | else if (strcmp(type, "sock") == 0) 159 | sprintf(log + offset, " %s:%d\n", str, port); 160 | else if (strcmp(type, "sockfd") == 0) 161 | sprintf(log + offset, " (sockfd)%d\n", sockfd); 162 | 163 | syslog(level, "%s", log); 164 | } 165 | 166 | void sock_debug(int sockfd, const char *fmt, ...) 167 | { 168 | va_list ap; 169 | 170 | va_start(ap, fmt); 171 | sock_print(sockfd, LOG_DEBUG, fmt, ap); 172 | va_end(ap); 173 | } 174 | 175 | void sock_info(int sockfd, const char *fmt, ...) 176 | { 177 | va_list ap; 178 | 179 | va_start(ap, fmt); 180 | sock_print(sockfd, LOG_INFO, fmt, ap); 181 | va_end(ap); 182 | } 183 | 184 | void sock_notice(int sockfd, const char *fmt, ...) 185 | { 186 | va_list ap; 187 | 188 | va_start(ap, fmt); 189 | sock_print(sockfd, LOG_NOTICE, fmt, ap); 190 | va_end(ap); 191 | } 192 | 193 | void sock_warn(int sockfd, const char *fmt, ...) 194 | { 195 | va_list ap; 196 | 197 | va_start(ap, fmt); 198 | sock_print(sockfd, LOG_WARNING, fmt, ap); 199 | va_end(ap); 200 | } 201 | 202 | void sock_err(int sockfd, const char *fmt, ...) 203 | { 204 | va_list ap; 205 | 206 | va_start(ap, fmt); 207 | sock_print(sockfd, LOG_ERR, fmt, ap); 208 | va_end(ap); 209 | } 210 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Zhao, Gang 3 | * This is free software; you can redistribute it and/or modify 4 | * it under the terms of the MIT license. See COPYING for details. 5 | */ 6 | 7 | #ifndef SS_LOG_H 8 | #define SS_LOG_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define pr_debug(fmt, args...) do {\ 19 | syslog(LOG_DEBUG, fmt, ## args); } while (0) 20 | #define pr_info(fmt, args...) do {\ 21 | syslog(LOG_INFO, fmt, ## args); } while (0) 22 | #define pr_notice(fmt, args...) do {\ 23 | syslog(LOG_NOTICE, fmt, ## args); } while (0) 24 | #define pr_warn(fmt, args...) do {\ 25 | syslog(LOG_WARNING, fmt, ## args); } while (0) 26 | #define pr_err(fmt, args...) do {\ 27 | syslog(LOG_ERR, fmt, ## args); } while (0) 28 | #define pr_exit(fmt, args...) do {\ 29 | syslog(LOG_ERR, fmt, ## args); exit(EXIT_FAILURE); } while (0) 30 | #define err_exit(msg) do {\ 31 | syslog(LOG_ERR, "%s: %s", msg, strerror(errno));\ 32 | exit(EXIT_FAILURE); } while (0) 33 | 34 | void pr_ai_debug(struct addrinfo *info, const char *fmt, ...); 35 | void pr_ai_info(struct addrinfo *info, const char *fmt, ...); 36 | void pr_ai_notice(struct addrinfo *info, const char *fmt, ...); 37 | void pr_ai_warn(struct addrinfo *info, const char *fmt, ...); 38 | void sock_debug(int sockfd, const char *fmt, ...); 39 | void sock_info(int sockfd, const char *fmt, ...); 40 | void sock_notice(int sockfd, const char *fmt, ...); 41 | void sock_warn(int sockfd, const char *fmt, ...); 42 | void sock_err(int sockfd, const char *fmt, ...); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /packages/shadowsocks-client-AA/Makefile: -------------------------------------------------------------------------------- 1 | include $(TOPDIR)/rules.mk 2 | 3 | PKG_NAME:=shadowsocks-client 4 | PKG_VERSION:=0.5 5 | PKG_RELEASE=$(PKG_SOURCE_VERSION) 6 | 7 | PKG_SOURCE_PROTO:=git 8 | PKG_SOURCE_URL:=http://github.com/zhao-gang/shadowsocks-tiny.git 9 | PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) 10 | PKG_SOURCE_VERSION:=d8ef02715f40de0fb7ba0f7267d3f8260f38ba80 11 | PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz 12 | PKG_MAINTAINER:=Zhao, Gang 13 | 14 | PKG_LICENSE:=MIT 15 | PKG_LICENSE_FILES:=COPYING 16 | 17 | PKG_BUILD_PARALLEL:=1 18 | 19 | include $(INCLUDE_DIR)/package.mk 20 | 21 | define Package/shadowsocks-client 22 | SECTION:=net 23 | CATEGORY:=Network 24 | SUBMENU:=Web Servers/Proxies 25 | DEPENDS:=+libopenssl 26 | TITLE:=shadowsocks client for router 27 | endef 28 | 29 | define Package/shadowsocks-client/install 30 | $(INSTALL_DIR) $(1)/usr/bin 31 | $(INSTALL_BIN) $(PKG_BUILD_DIR)/sslocal $(1)/usr/bin/ 32 | $(INSTALL_DIR) $(1)/etc/config 33 | $(INSTALL_DATA) ./files/sslocal.config $(1)/etc/config/sslocal 34 | $(INSTALL_DIR) $(1)/etc/init.d 35 | $(INSTALL_BIN) ./files/sslocal.init $(1)/etc/init.d/sslocal 36 | endef 37 | 38 | $(eval $(call BuildPackage,shadowsocks-client)) 39 | -------------------------------------------------------------------------------- /packages/shadowsocks-client-AA/files/sslocal.config: -------------------------------------------------------------------------------- 1 | config sslocal 2 | option server_addr '' 3 | option server_port '' 4 | option local_addr '' 5 | option local_port '' 6 | option password '' 7 | option method '' 8 | -------------------------------------------------------------------------------- /packages/shadowsocks-client-AA/files/sslocal.init: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | # Copyright (C) 2006-2012 OpenWrt.org 3 | # Copyright (C) 2014 Zhao, Gang 4 | 5 | START=99 6 | 7 | PROG=/usr/bin/sslocal 8 | NAME=sslocal 9 | 10 | sslocal_instance() { 11 | local section="$1" 12 | local val 13 | 14 | config_get var "${section}" server_addr 15 | append args "-s ${var}" 16 | config_get var "${section}" server_port 17 | append args "-p ${var}" 18 | config_get var "${section}" local_addr 19 | append args "-u ${var}" 20 | config_get var "${section}" local_port 21 | append args "-b ${var}" 22 | config_get var "${section}" password 23 | append args "-k ${var}" 24 | config_get var "${section}" method 25 | append args "-m ${var}" 26 | config_get var "${section}" log_level 5 27 | append args "-l ${var}" 28 | append args "-d" 29 | service_start ${PROG} ${args} 30 | } 31 | 32 | start() { 33 | config_load "${NAME}" 34 | 35 | config_foreach sslocal_instance sslocal 36 | } 37 | 38 | stop() { 39 | service_stop ${PROG} 40 | } 41 | -------------------------------------------------------------------------------- /packages/shadowsocks-client/Makefile: -------------------------------------------------------------------------------- 1 | include $(TOPDIR)/rules.mk 2 | 3 | PKG_NAME:=shadowsocks-client 4 | PKG_VERSION:=0.5 5 | PKG_RELEASE=$(PKG_SOURCE_VERSION) 6 | 7 | PKG_SOURCE_PROTO:=git 8 | PKG_SOURCE_URL:=http://github.com/zhao-gang/shadowsocks-tiny.git 9 | PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) 10 | PKG_SOURCE_VERSION:=d8ef02715f40de0fb7ba0f7267d3f8260f38ba80 11 | PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz 12 | PKG_MAINTAINER:=Zhao, Gang 13 | 14 | PKG_LICENSE:=MIT 15 | PKG_LICENSE_FILES:=COPYING 16 | 17 | PKG_BUILD_PARALLEL:=1 18 | 19 | include $(INCLUDE_DIR)/package.mk 20 | 21 | define Package/shadowsocks-client 22 | SECTION:=net 23 | CATEGORY:=Network 24 | SUBMENU:=Web Servers/Proxies 25 | TITLE:=shadowsocks client for router 26 | URL:=https://github.com/zhao-gang/shadowsocks-tiny 27 | DEPENDS:=+libopenssl 28 | endef 29 | 30 | define Package/shadowsocks-client/install 31 | $(INSTALL_DIR) $(1)/usr/bin 32 | $(INSTALL_BIN) $(PKG_BUILD_DIR)/sslocal $(1)/usr/bin/ 33 | $(INSTALL_DIR) $(1)/etc/config 34 | $(INSTALL_DATA) ./files/sslocal.config $(1)/etc/config/sslocal 35 | $(INSTALL_DIR) $(1)/etc/init.d 36 | $(INSTALL_BIN) ./files/sslocal.init $(1)/etc/init.d/sslocal 37 | endef 38 | 39 | $(eval $(call BuildPackage,shadowsocks-client)) 40 | -------------------------------------------------------------------------------- /packages/shadowsocks-client/files/sslocal.config: -------------------------------------------------------------------------------- 1 | config sslocal 2 | option server_addr '' 3 | option server_port '' 4 | option local_addr '' 5 | option local_port '' 6 | option password '' 7 | option method '' 8 | -------------------------------------------------------------------------------- /packages/shadowsocks-client/files/sslocal.init: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | # Copyright (C) 2006-2012 OpenWrt.org 3 | # Copyright (C) 2014 Zhao, Gang 4 | 5 | START=99 6 | 7 | USE_PROCD=1 8 | PROG=/usr/bin/sslocal 9 | 10 | validate_section_sslocal() { 11 | uci_validate_section sslocal sslocal "${1}" \ 12 | 'server_addr:host' \ 13 | 'server_port:port' \ 14 | 'local_addr:host' \ 15 | 'local_port:port' \ 16 | 'password:string' \ 17 | 'method:string' \ 18 | 'log_level:range(0,7):5' 19 | 20 | return $? 21 | } 22 | 23 | sslocal_instance() { 24 | local server_addr server_port local_addr local_port 25 | local password method log_level 26 | 27 | validate_section_sslocal "${1}" || { 28 | echo "validation failed" 29 | return 1 30 | } 31 | 32 | procd_open_instance 33 | procd_set_param command "$PROG" 34 | procd_append_param command -s "${server_addr}" -p "${server_port}" 35 | procd_append_param command -u "${local_addr}" -b "${local_port}" 36 | procd_append_param command -k "${password}" -m "${method}" 37 | procd_append_param command -l "${log_level}" 38 | procd_set_param respawn 39 | procd_close_instance 40 | } 41 | 42 | start_service() { 43 | config_load sslocal 44 | 45 | config_foreach sslocal_instance sslocal 46 | } 47 | 48 | service_triggers() 49 | { 50 | procd_add_reload_trigger "sslocal" 51 | procd_add_validation validate_section_sslocal 52 | } 53 | -------------------------------------------------------------------------------- /server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Zhao, Gang 3 | * This is free software; you can redistribute it and/or modify 4 | * it under the terms of the MIT license. See COPYING for details. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "common.h" 17 | #include "crypto.h" 18 | #include "log.h" 19 | 20 | /* read text from remote, encrypt and send to local */ 21 | int server_do_remote_read(int sockfd, struct link *ln) 22 | { 23 | int ret; 24 | 25 | if (ln->state & SERVER_SEND_PENDING) 26 | return 0; 27 | 28 | ret = do_read(sockfd, ln, "text", 0); 29 | if (ret == -2) { 30 | goto out; 31 | } else if (ret == -1) { 32 | return 0; 33 | } 34 | 35 | if (ln->state & SS_UDP) { 36 | goto out; 37 | } 38 | 39 | if (crypto_encrypt(sockfd, ln) == -1) 40 | goto out; 41 | 42 | ret = do_send(ln->local_sockfd, ln, "cipher", 0); 43 | if (ret == -2) { 44 | goto out; 45 | } else if (ret == -1) { 46 | ln->state |= LOCAL_SEND_PENDING; 47 | } 48 | 49 | return 0; 50 | out: 51 | return -1; 52 | } 53 | 54 | /* read cipher from local, decrypt and send to server */ 55 | int server_do_local_read(int sockfd, struct link *ln) 56 | { 57 | int ret; 58 | 59 | if (ln->state & LOCAL_SEND_PENDING) { 60 | return 0; 61 | } 62 | 63 | /* if iv isn't received, wait to receive bigger than iv_len 64 | * bytes before go to next step */ 65 | if (ln->state & LOCAL_READ_PENDING) { 66 | ret = do_read(sockfd, ln, "cipher", ln->cipher_len); 67 | if (ret == -2) { 68 | goto out; 69 | } else if (ret == -1) { 70 | return 0; 71 | } 72 | 73 | if (ln->cipher_len <= iv_len) { 74 | return 0; 75 | } else { 76 | ln->state &= ~SERVER_READ_PENDING; 77 | } 78 | } else { 79 | ret = do_read(sockfd, ln, "cipher", 0); 80 | if (ret == -2) { 81 | goto out; 82 | } else if (ret == -1) { 83 | return 0; 84 | } 85 | 86 | if (!(ln->state & SS_IV_RECEIVED)) { 87 | if (ln->cipher_len <= iv_len) { 88 | ln->state |= LOCAL_READ_PENDING; 89 | return 0; 90 | } 91 | } 92 | } 93 | 94 | if (crypto_decrypt(sockfd, ln) == -1) 95 | goto out; 96 | 97 | if (ln->state & SS_UDP) { 98 | if (check_ss_header(sockfd, ln) == -1) 99 | goto out; 100 | } else if (!(ln->state & SS_TCP_HEADER_RECEIVED)) { 101 | if (check_ss_header(sockfd, ln) == -1) 102 | goto out; 103 | 104 | ln->state |= SS_TCP_HEADER_RECEIVED; 105 | 106 | if (ln->text_len == 0) 107 | return 0; 108 | } 109 | 110 | ret = do_send(ln->server_sockfd, ln, "text", 0); 111 | if (ret == -2) { 112 | goto out; 113 | } else if (ret == -1) { 114 | ln->state |= SERVER_SEND_PENDING; 115 | } 116 | 117 | return 0; 118 | out: 119 | return -1; 120 | } 121 | 122 | int server_do_pollin(int sockfd, struct link *ln) 123 | { 124 | if (sockfd == ln->local_sockfd) { 125 | if (ln->state & SERVER_PENDING) { 126 | sock_info(sockfd, "%s: server pending", 127 | __func__); 128 | goto out; 129 | } else if (server_do_local_read(sockfd, ln) == -1) { 130 | goto clean; 131 | } else { 132 | goto out; 133 | } 134 | } else if (sockfd == ln->server_sockfd) { 135 | if (ln->state & LOCAL_PENDING) { 136 | sock_info(sockfd, "%s: local pending", 137 | __func__); 138 | goto out; 139 | } else if (server_do_remote_read(sockfd, ln) == -1) { 140 | goto clean; 141 | } else { 142 | goto out; 143 | } 144 | } 145 | 146 | out: 147 | return 0; 148 | clean: 149 | sock_info(sockfd, "%s: close", __func__); 150 | destroy_link(sockfd); 151 | return -1; 152 | } 153 | 154 | int server_do_pollout(int sockfd, struct link *ln) 155 | { 156 | int optval, ret; 157 | int optlen = sizeof(optval); 158 | 159 | /* write to local */ 160 | if (sockfd == ln->local_sockfd) { 161 | if (ln->state & LOCAL_SEND_PENDING) { 162 | ret = do_send(sockfd, ln, "cipher", 0); 163 | if (ret == -2) { 164 | goto clean; 165 | } else if (ret == -1) { 166 | goto out; 167 | } else { 168 | ln->state &= ~LOCAL_SEND_PENDING; 169 | goto out; 170 | } 171 | } else { 172 | poll_rm(sockfd, POLLOUT); 173 | } 174 | } else { 175 | /* pending connect finished */ 176 | if (!(ln->state & SERVER)) { 177 | if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, 178 | &optval, (void *)&optlen) == -1) { 179 | sock_warn(sockfd, "%s: getsockopt() %s", 180 | __func__, strerror(errno)); 181 | return -1; 182 | } 183 | 184 | if (optval == 0) { 185 | sock_info(sockfd, 186 | "%s: pending connect() finished", 187 | __func__); 188 | ln->time = time(NULL); 189 | ln->state |= SERVER; 190 | } else { 191 | sock_warn(sockfd, 192 | "%s: pending connect() failed", 193 | __func__); 194 | goto clean; 195 | } 196 | } 197 | 198 | if (ln->state & SERVER_SEND_PENDING) { 199 | ret = do_send(sockfd, ln, "text", 0); 200 | if (ret == -2) { 201 | goto clean; 202 | } else if (ret == -1) { 203 | goto out; 204 | } else { 205 | ln->state &= ~SERVER_SEND_PENDING; 206 | goto out; 207 | } 208 | } else { 209 | poll_rm(sockfd, POLLOUT); 210 | } 211 | } 212 | 213 | out: 214 | return 0; 215 | clean: 216 | sock_info(sockfd, "%s: close:", __func__); 217 | destroy_link(sockfd); 218 | return -1; 219 | } 220 | 221 | int main(int argc, char **argv) 222 | { 223 | short revents; 224 | int i, listenfd, sockfd; 225 | int ret = 0; 226 | struct link *ln; 227 | struct addrinfo *local_ai_tcp = NULL; 228 | struct addrinfo *local_ai_udp = NULL; 229 | struct addrinfo hint; 230 | 231 | check_ss_option(argc, argv, "server"); 232 | 233 | memset(&hint, 0, sizeof(hint)); 234 | hint.ai_family = AF_UNSPEC; 235 | hint.ai_socktype = SOCK_STREAM; 236 | 237 | ret = getaddrinfo(ss_opt.local_addr, ss_opt.local_port, 238 | &hint, &local_ai_tcp); 239 | if (ret != 0) { 240 | printf("getaddrinfo error: %s\n", gai_strerror(ret)); 241 | ret = -1; 242 | goto out; 243 | } 244 | 245 | pr_ai_notice(local_ai_tcp, "listening tcp address"); 246 | 247 | hint.ai_socktype = SOCK_DGRAM; 248 | ret = getaddrinfo(ss_opt.local_addr, ss_opt.local_port, 249 | &hint, &local_ai_udp); 250 | if (ret != 0) { 251 | printf("getaddrinfo error: %s\n", gai_strerror(ret)); 252 | ret = -1; 253 | goto out; 254 | } 255 | 256 | pr_ai_notice(local_ai_udp, "udp address"); 257 | 258 | if (crypto_init(ss_opt.password, ss_opt.method) == -1) { 259 | ret = -1; 260 | goto out; 261 | } 262 | 263 | ss_init(); 264 | listenfd = do_listen(local_ai_tcp, "tcp"); 265 | clients[0].fd = listenfd; 266 | clients[0].events = POLLIN; 267 | listenfd = do_listen(local_ai_udp, "udp"); 268 | clients[1].fd = listenfd; 269 | clients[1].events = POLLIN; 270 | 271 | while (1) { 272 | pr_debug("start polling\n"); 273 | ret = poll(clients, nfds, TCP_INACTIVE_TIMEOUT * 1000); 274 | if (ret == -1) { 275 | err_exit("poll error"); 276 | } else if (ret == 0) { 277 | reaper(); 278 | continue; 279 | } 280 | 281 | if (clients[0].revents & POLLIN) { 282 | sockfd = accept(clients[0].fd, NULL, NULL); 283 | if (sockfd == -1) { 284 | pr_warn("accept error\n"); 285 | } else if (poll_set(sockfd, POLLIN) == -1) { 286 | close(sockfd); 287 | } else { 288 | ln = create_link(sockfd, "server"); 289 | if (ln == NULL) { 290 | poll_del(sockfd); 291 | close(sockfd); 292 | } 293 | } 294 | } 295 | 296 | if (clients[1].revents & POLLIN) { 297 | pr_warn("udp socks5 not supported(for now)\n"); 298 | /* ln = create_link(sockfd, "server"); */ 299 | /* if (ln != NULL) { */ 300 | /* check_ss_header(sockfd, ln); */ 301 | /* } */ 302 | } 303 | 304 | for (i = 2; i < nfds; i++) { 305 | sockfd = clients[i].fd; 306 | if (sockfd == -1) 307 | continue; 308 | 309 | revents = clients[i].revents; 310 | if (revents == 0) 311 | continue; 312 | 313 | ln = get_link(sockfd); 314 | if (ln == NULL) { 315 | sock_warn(sockfd, "close: can't get link"); 316 | close(sockfd); 317 | continue; 318 | } 319 | 320 | if (revents & POLLIN) { 321 | server_do_pollin(sockfd, ln); 322 | } 323 | 324 | if (revents & POLLOUT) { 325 | server_do_pollout(sockfd, ln); 326 | } 327 | 328 | /* suppress the noise */ 329 | /* if (revents & POLLPRI) { */ 330 | /* sock_warn(sockfd, "POLLERR"); */ 331 | /* } else if (revents & POLLERR) { */ 332 | /* sock_warn(sockfd, "POLLERR"); */ 333 | /* } else if (revents & POLLHUP) { */ 334 | /* sock_warn(sockfd, "POLLHUP"); */ 335 | /* } else if (revents & POLLNVAL) { */ 336 | /* sock_warn(sockfd, "POLLNVAL"); */ 337 | /* } */ 338 | } 339 | 340 | reaper(); 341 | } 342 | 343 | out: 344 | crypto_exit(); 345 | 346 | if (local_ai_tcp) 347 | freeaddrinfo(local_ai_tcp); 348 | 349 | if (local_ai_udp) 350 | freeaddrinfo(local_ai_udp); 351 | 352 | ss_exit(); 353 | 354 | if (ret == -1) 355 | exit(EXIT_FAILURE); 356 | else 357 | exit(EXIT_SUCCESS); 358 | } 359 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Zhao, Gang 3 | * This is free software; you can redistribute it and/or modify 4 | * it under the terms of the MIT license. See COPYING for details. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | #include "common.h" 20 | #include "crypto.h" 21 | #include "log.h" 22 | 23 | int main(int argc, char **argv) 24 | { 25 | return 0; 26 | } 27 | --------------------------------------------------------------------------------