├── .gitignore ├── Makefile ├── README.md ├── buff.c ├── buff.h ├── screenshot ├── 1.png └── 2.png └── socks5.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | socks5 3 | core.* 4 | vgcore.* 5 | 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | cc = gcc 2 | flag = -Wall -g -std=c99 3 | target = socks5 4 | object = socks5.o buff.o 5 | src = socks5.c buff.c 6 | 7 | all : $(object) 8 | $(cc) $(flag) -o $(target) $(object) 9 | 10 | socks5.o : socks5.c 11 | $(cc) $(flag) -c -o socks5.o socks5.c 12 | 13 | buff.o : buff.c 14 | $(cc) $(flag) -c -o buff.o buff.c 15 | 16 | .PHONY: clean 17 | 18 | clean: 19 | rm *.o $(target) core.* vgcore.* 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # socks5 2 | A light weight socks5 proxy server implemented in c using epoll and nonblocking socket.\ 3 | You can use it to surf the Internet scientifically, only UNIX/Linux platforms are supported currently. 4 | ## Suport 5 | - No authentication 6 | - Username & Password authentication 7 | - Ipv4 & Ipv6 8 | - Domain name resolution 9 | - "CONNECT" command in socks5 protocol 10 | ## Build 11 | ``` 12 | make 13 | ``` 14 | ## Usage 15 | ``` 16 | -a : ip address 17 | -p : port 18 | -u : username 19 | -k : password 20 | ``` 21 | ## Example 22 | 1.Run the server. 23 | ``` 24 | ./socks5 -a 192.168.1.40 -p 6080 -u abc123 -p qwe123 25 | ``` 26 | 2.Run a client such as Proxifier in windows.\ 27 | ![](https://github.com/bhhbazinga/socks5/blob/master/screenshot/1.png) 28 | 3.Configure a socks5 proxy server using the address and port above.\ 29 | ![](https://github.com/bhhbazinga/socks5/blob/master/screenshot/2.png) 30 | 4.Change the proxification rules using the socks5 proxy server we just configured.\ 31 | If you see the traffic passing through the socks5 proxy server, you succeeded.\ 32 | Enjoy it! 33 | ## TODO 34 | - Asynchronous DNS resolutiuon and Cache 35 | - A socks5 client that connects the browser to the server 36 | - Encrypted transmitting 37 | - "BIND" and "ASSOCIATE" command may be supported 38 | -------------------------------------------------------------------------------- /buff.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "buff.h" 7 | 8 | /* +-------------+------------------+------------------+ 9 | * | prependable | readable | writable | 10 | * +-------------+------------------+------------------+ 11 | * read_index writable_index cap 12 | */ 13 | 14 | buff_t* buff_create(size_t cap) 15 | { 16 | buff_t *buff = (buff_t*)malloc(sizeof(*buff)); 17 | if (buff == NULL) return NULL; 18 | memset(buff, 0, sizeof(*buff)); 19 | 20 | char *data = (char*)malloc(cap); 21 | if (data == NULL) { 22 | free(buff); 23 | return NULL; 24 | } 25 | 26 | buff->write_index = 0; 27 | buff->read_index = 0; 28 | buff->cap = cap; 29 | buff->data = data; 30 | 31 | return buff; 32 | } 33 | 34 | void buff_release(buff_t *buff) 35 | { 36 | free(buff->data); 37 | free(buff); 38 | } 39 | 40 | size_t buff_readable(buff_t *buff) 41 | { 42 | return buff->write_index - buff->read_index; 43 | } 44 | 45 | static size_t buff_prependable(buff_t* buff) 46 | { 47 | return buff->read_index; 48 | } 49 | 50 | 51 | static size_t buff_writable(buff_t *buff) 52 | { 53 | return buff->cap - buff->write_index; 54 | } 55 | 56 | static int buff_expand(buff_t *buff) 57 | { 58 | int newcap = buff->cap * 2; 59 | char *newdata = realloc(buff->data, newcap); 60 | if (newdata == NULL) return -1; 61 | 62 | buff->cap = newcap; 63 | buff->data = newdata; 64 | return 0; 65 | } 66 | 67 | int buff_readfd(buff_t *buff, int fd) 68 | { 69 | int writable = buff_writable(buff); 70 | if (writable <= 0) { 71 | assert(writable == 0); 72 | 73 | if (buff_expand(buff) < 0) return -1; 74 | writable = buff_writable(buff); 75 | } 76 | 77 | int n = read(fd, buff->data + buff->write_index, writable); 78 | if (n <= 0) return n; 79 | 80 | buff->write_index += n; 81 | return n; 82 | } 83 | 84 | int buff_writefd(buff_t *buff, int fd) 85 | { 86 | int readable = buff_readable(buff); 87 | int n = write(fd, buff->data + buff->read_index, readable); 88 | if (n <= 0) return n; 89 | 90 | buff->read_index += n; 91 | return n; 92 | } 93 | 94 | void* buff_read(buff_t *buff, void *dst, size_t size) 95 | { 96 | size_t readable = buff_readable(buff); 97 | assert(size <= readable); 98 | 99 | memcpy(dst, buff->data + buff->read_index, size); 100 | buff->read_index += size; 101 | return dst; 102 | } 103 | 104 | void buff_skip(buff_t *buff, size_t size) 105 | { 106 | size_t readable = buff_readable(buff); 107 | assert(size <= readable); 108 | 109 | buff->read_index += size; 110 | } 111 | 112 | /* 113 | * 1. If writable >= size then append to buff 114 | * 2. If prepandable + writable >= size then move readable content to 0 115 | * 3. Otherwise, expand buff 116 | * 117 | * repeat until 1 or 2 satisfied 118 | */ 119 | int buff_write(buff_t *buff, void *src, size_t size) 120 | { 121 | for(;;) { 122 | size_t writable = buff_writable(buff); 123 | if (writable >= size) break; 124 | 125 | size_t prependable = buff_prependable(buff); 126 | if (prependable + writable >= size) { // move readable content to 0 127 | int readable = buff_readable(buff); 128 | memmove(buff->data, buff->data + buff->read_index, readable); 129 | buff->read_index = 0; 130 | buff->write_index -= prependable; 131 | break; 132 | } 133 | 134 | if (buff_expand(buff) < 0) return -1; 135 | } 136 | 137 | memcpy(buff->data + buff->write_index, src, size); 138 | buff->write_index += size; 139 | return 0; 140 | } 141 | 142 | int buff_concat(buff_t *front, buff_t *rear) 143 | { 144 | return buff_write(front, rear->data + rear->read_index, buff_readable(rear)); 145 | } 146 | 147 | void buff_clear(buff_t *buff) 148 | { 149 | buff->write_index = 0; 150 | buff->read_index = 0; 151 | } 152 | -------------------------------------------------------------------------------- /buff.h: -------------------------------------------------------------------------------- 1 | #ifndef BUFF_H 2 | #define BUFF_H 3 | 4 | #include 5 | 6 | typedef struct buff { 7 | char *data; 8 | size_t write_index; 9 | size_t read_index; 10 | size_t cap; 11 | } buff_t; 12 | 13 | buff_t* buff_create(size_t cap); 14 | void buff_release(buff_t *buff); 15 | int buff_readfd(buff_t *buff, int fd); 16 | int buff_writefd(buff_t *buff, int fd); 17 | void* buff_read(buff_t *buff, void *dst, size_t size); 18 | void buff_skip(buff_t *buff, size_t size); 19 | int buff_write(buff_t *buff, void *src, size_t size); 20 | int buff_concat(buff_t *front, buff_t *rear); 21 | size_t buff_readable(buff_t *buff); 22 | void buff_clear(buff_t *buff); 23 | 24 | #endif // BUFF_H 25 | -------------------------------------------------------------------------------- /screenshot/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhhbazinga/socks5/9deefb1c6b98ac6ec83ef834c65b754a5adf7cc2/screenshot/1.png -------------------------------------------------------------------------------- /screenshot/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhhbazinga/socks5/9deefb1c6b98ac6ec83ef834c65b754a5adf7cc2/screenshot/2.png -------------------------------------------------------------------------------- /socks5.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200112L 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "buff.h" 19 | 20 | #if (EAGAIN != EWOULDBLOCK) 21 | #define EAGAIN_EWOULDBLOCK EAGAIN : case EWOULDBLOCK 22 | #else 23 | #define EAGAIN_EWOULDBLOCK EAGAIN 24 | #endif 25 | 26 | #define LOG(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__); fprintf(stderr, "\n") 27 | 28 | typedef struct sockaddr sockaddr_t; 29 | typedef struct sockaddr_in sockaddr_in_t; 30 | typedef struct sockaddr_in6 sockaddr_in6_t; 31 | typedef struct addrinfo addrinfo_t; 32 | typedef struct epoll_event epoll_event_t; 33 | 34 | typedef void read_cb(int fd, void *ud); 35 | typedef void write_cb(int fd, void *ud); 36 | 37 | #define INIT_BUFF_CAP 1024 38 | 39 | #define MAX_UNAME_LEN 20 40 | #define MAX_PASSWD_LEN 20 41 | #define BLACKLOG 1024 42 | #define MAX_EPOLL_EVENTS 64 43 | 44 | typedef enum tunnel_state { 45 | open_state, 46 | auth_state, 47 | request_state, 48 | connecting_state, // connecting to remote 49 | connected_state, // connected to remote 50 | } tunnel_state_t; 51 | 52 | typedef struct open_protocol { 53 | uint8_t ver; 54 | uint8_t nmethods; 55 | uint8_t methods[255]; 56 | } open_protocol_t; 57 | 58 | typedef struct auth_protocol { 59 | uint8_t ver; 60 | uint8_t ulen; 61 | char uname[255]; 62 | uint8_t plen; 63 | char passwd[255]; 64 | } auth_protocol_t; 65 | 66 | typedef struct request_protocol { 67 | uint8_t ver; 68 | uint8_t cmd; 69 | uint8_t rsv; 70 | uint8_t atyp; 71 | uint8_t domainlen; 72 | char addr[255]; 73 | uint16_t port; 74 | } request_protocol_t; 75 | 76 | typedef struct sock sock_t; 77 | typedef struct tunnel { 78 | sock_t *client_sock; 79 | sock_t *remote_sock; 80 | 81 | tunnel_state_t state; 82 | open_protocol_t op; 83 | auth_protocol_t ap; 84 | request_protocol_t rp; 85 | size_t read_count; 86 | int closed; 87 | } tunnel_t; 88 | 89 | typedef enum sock_state { 90 | sock_connecting, 91 | sock_connected, 92 | sock_halfclosed, 93 | sock_closed, 94 | } sock_state_t; 95 | 96 | struct sock { 97 | int fd; 98 | read_cb *read_handle; 99 | write_cb *write_handle; 100 | buff_t *read_buff; 101 | buff_t *write_buff; 102 | tunnel_t *tunnel; 103 | sock_state_t state; 104 | int isclient; 105 | }; 106 | 107 | typedef struct server { 108 | int listenfd; 109 | read_cb *read_handle; 110 | int epollfd; 111 | char username[255]; 112 | char passwd[255]; 113 | } server_t; 114 | 115 | server_t SERVER; 116 | 117 | 118 | static tunnel_t* tunnel_create(int cap); 119 | static void tunnel_release(tunnel_t *tunnel); 120 | static void tunnel_shutdown(tunnel_t *tunnel); 121 | static void tunnel_read_handle(int fd, void *ud); 122 | static void tunnel_write_handle(int fd, void *ud); 123 | static int tunnel_open_handle(tunnel_t *tunnel); 124 | static int tunnel_auth_handle(tunnel_t *tunnel); 125 | static int tunnel_request_handle(tunnel_t *tunnel); 126 | static int tunnel_connecting_handle(tunnel_t *tunnel); 127 | static int tunnel_connected_handle(tunnel_t *tunnel, int client); 128 | static int tunnel_write_client(tunnel_t *tunnel, void *src, size_t size); 129 | 130 | static int epoll_add(sock_t *sock) 131 | { 132 | epoll_event_t event; 133 | event.events = EPOLLIN; 134 | event.data.ptr = sock; 135 | return epoll_ctl(SERVER.epollfd, EPOLL_CTL_ADD, sock->fd, &event); 136 | } 137 | 138 | static int epoll_del(sock_t *sock) 139 | { 140 | epoll_event_t event; 141 | return epoll_ctl(SERVER.epollfd, EPOLL_CTL_DEL, sock->fd, &event); 142 | } 143 | 144 | static int epoll_modify(sock_t *sock, int writable, int readable) 145 | { 146 | epoll_event_t event; 147 | event.data.ptr = sock; 148 | event.events = (writable ? EPOLLOUT : 0) | (readable ? EPOLLIN : 0); 149 | return epoll_ctl(SERVER.epollfd, EPOLL_CTL_MOD, sock->fd, &event); 150 | } 151 | 152 | static sock_t* sock_create(int fd, sock_state_t state, int isclient, tunnel_t * tunnel) 153 | { 154 | sock_t *sock = (sock_t*)malloc(sizeof(*sock)); 155 | if (sock == NULL) return NULL; 156 | memset(sock, 0, sizeof(*sock)); 157 | 158 | buff_t *read_buff = buff_create(INIT_BUFF_CAP); 159 | if (read_buff == NULL) return NULL; 160 | 161 | buff_t *write_buff = buff_create(INIT_BUFF_CAP); 162 | if (write_buff == NULL) { 163 | buff_release(read_buff); 164 | free(sock); 165 | return NULL; 166 | } 167 | 168 | sock->read_buff = read_buff; 169 | sock->write_buff = write_buff; 170 | sock->tunnel = tunnel; 171 | sock->fd = fd; 172 | sock->read_handle = tunnel_read_handle; 173 | sock->write_handle = tunnel_write_handle; 174 | sock->state = state; 175 | sock->isclient = isclient; 176 | return sock; 177 | } 178 | 179 | static void sock_release(sock_t *sock) 180 | { 181 | tunnel_t *tunnel = sock->tunnel; 182 | 183 | buff_release(sock->write_buff); 184 | buff_release(sock->read_buff); 185 | 186 | if (sock->isclient) tunnel->client_sock = NULL; 187 | else tunnel->remote_sock = NULL; 188 | epoll_del(sock); 189 | close(sock->fd); 190 | free(sock); 191 | 192 | // when both client and remote sock release 193 | // release tunnel 194 | if (tunnel->remote_sock == NULL && tunnel->client_sock == NULL) { 195 | tunnel_release(tunnel); 196 | } 197 | } 198 | 199 | /* 200 | * Receive rst or no more data to send or invalid peer, we should release sock 201 | * */ 202 | static void sock_force_shutdown(sock_t *sock) 203 | { 204 | sock_release(sock); 205 | } 206 | 207 | /* 208 | * Receive fin, do not receive again, 209 | * If Append read_buff to other write_buff, 210 | * If write_buff not empty, we shoould still send data, 211 | * Otherwise force shutdown 212 | * */ 213 | static void sock_shutdown(sock_t *sock) 214 | { 215 | sock->state = sock_halfclosed; 216 | 217 | tunnel_t *tunnel = sock->tunnel; 218 | // forward left data 219 | if (tunnel->state == connected_state) { 220 | if (sock->isclient && tunnel->remote_sock != NULL) 221 | buff_concat(tunnel->remote_sock->write_buff, sock->read_buff); 222 | else if(tunnel->client_sock != NULL) 223 | buff_concat(tunnel->client_sock->write_buff, sock->read_buff); 224 | } 225 | 226 | int writable = buff_readable(sock->write_buff) > 0; 227 | if (writable) epoll_modify(sock, writable, 0); 228 | else sock_force_shutdown(sock); 229 | } 230 | 231 | static int sock_nonblocking(int fd) 232 | { 233 | int flag; 234 | if ((flag = fcntl(fd, F_GETFL, 0)) < 0) return -1; 235 | if ((flag = fcntl(fd, F_SETFL, flag | O_NONBLOCK)) < 0) return -1; 236 | return flag; 237 | } 238 | 239 | static int sock_keepalive(int fd) 240 | { 241 | int keepalive = 1; 242 | return setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)); 243 | } 244 | 245 | static tunnel_t* tunnel_create(int fd) 246 | { 247 | sock_nonblocking(fd); 248 | sock_keepalive(fd); 249 | 250 | tunnel_t *tunnel = (tunnel_t*)malloc(sizeof(*tunnel)); 251 | if (tunnel == NULL) { 252 | close(fd); 253 | return NULL; 254 | } 255 | memset(tunnel, 0, sizeof(*tunnel)); 256 | 257 | sock_t *client_sock = sock_create(fd, sock_connected, 1, tunnel); 258 | if (client_sock == NULL) { 259 | free(tunnel); 260 | close(fd); 261 | return NULL; 262 | } 263 | 264 | tunnel->state = open_state; 265 | tunnel->client_sock = client_sock; 266 | tunnel->read_count = 0; 267 | tunnel->closed = 0; 268 | 269 | epoll_add(client_sock); 270 | 271 | return tunnel; 272 | } 273 | 274 | static void tunnel_shutdown(tunnel_t *tunnel) 275 | { 276 | if (tunnel->client_sock != NULL) sock_shutdown(tunnel->client_sock); 277 | if (tunnel->remote_sock != NULL) sock_shutdown(tunnel->remote_sock); 278 | } 279 | 280 | static void tunnel_release(tunnel_t *tunnel) 281 | { 282 | free(tunnel); 283 | } 284 | 285 | static void tunnel_read_handle(int fd, void *ud) 286 | { 287 | sock_t *sock = (sock_t*)ud; 288 | tunnel_t *tunnel = sock->tunnel; 289 | 290 | int n = buff_readfd(sock->read_buff, fd); 291 | if (n < 0) { 292 | switch (errno) { 293 | case EINTR: 294 | case EAGAIN_EWOULDBLOCK: 295 | break; 296 | default: 297 | goto shutdown; 298 | } 299 | 300 | } else if (n == 0) goto shutdown; 301 | 302 | switch(tunnel->state) { 303 | case open_state: 304 | if (tunnel_open_handle(tunnel) < 0) goto force_shutdown; 305 | break; 306 | case auth_state: 307 | if (tunnel_auth_handle(tunnel) < 0) goto force_shutdown; 308 | break; 309 | case request_state: 310 | if (tunnel_request_handle(tunnel) < 0) goto force_shutdown; 311 | break; 312 | case connecting_state: 313 | assert(sock->isclient == 0); 314 | if (tunnel_connecting_handle(tunnel) < 0) goto tunnel_shutdown; 315 | break; 316 | case connected_state: 317 | if (tunnel_connected_handle(tunnel, sock->isclient) < 0) goto tunnel_shutdown; 318 | break; 319 | default: 320 | assert(0); 321 | break; 322 | } 323 | return; 324 | 325 | force_shutdown: // peer invalid 326 | sock_force_shutdown(sock); 327 | return; 328 | 329 | shutdown: // half closed 330 | sock_shutdown(sock); 331 | return; 332 | 333 | tunnel_shutdown: // half closed both client and remote 334 | tunnel_shutdown(tunnel); 335 | } 336 | 337 | static void tunnel_write_handle(int fd, void *ud) 338 | { 339 | sock_t *sock = (sock_t *)ud; 340 | tunnel_t *tunnel = sock->tunnel; 341 | 342 | if (buff_readable(sock->write_buff) > 0) { 343 | int n = buff_writefd(sock->write_buff, fd); 344 | if (n <= 0) { 345 | switch (errno) { 346 | case EINTR: 347 | case EAGAIN_EWOULDBLOCK: 348 | break; 349 | default: 350 | goto force_shutdown; 351 | } 352 | } 353 | } else if (sock->state == sock_halfclosed) { 354 | goto force_shutdown; 355 | } 356 | 357 | if (tunnel->state == connecting_state) { 358 | assert(sock->isclient == 0); 359 | 360 | if (tunnel_connecting_handle(tunnel) < 0) goto tunnel_shutdown; 361 | } 362 | 363 | int writable = buff_readable(sock->write_buff) > 0; 364 | epoll_modify(sock, writable, 1); 365 | 366 | return; 367 | 368 | tunnel_shutdown: 369 | tunnel_shutdown(tunnel); 370 | return; 371 | 372 | force_shutdown: 373 | sock_force_shutdown(sock); 374 | return; 375 | } 376 | 377 | // |VER(1)|NMETHODS(1)|METHODS(1-255)| 378 | static int tunnel_open_handle(tunnel_t* tunnel) 379 | { 380 | buff_t *buff = tunnel->client_sock->read_buff; 381 | open_protocol_t *op = &tunnel->op; 382 | size_t *nreaded = &tunnel->read_count; 383 | size_t nheader = sizeof(op->ver) + sizeof(op->nmethods); 384 | 385 | if (*nreaded == 0) goto header; 386 | else if(*nreaded == nheader) goto methods; 387 | else assert(0); 388 | 389 | header: 390 | // VER(1)|NMETHODS(1) 391 | if (buff_readable(buff) >= nheader) { 392 | buff_read(buff, &op->ver, sizeof(op->ver)); 393 | if (op->ver != 0x05) return -1; 394 | 395 | buff_read(buff, &op->nmethods, sizeof(op->nmethods)); 396 | *nreaded += nheader; 397 | } else return 0; 398 | 399 | methods: 400 | // METHODS(1-255) 401 | if (buff_readable(buff) >= op->nmethods) { 402 | buff_read(buff, op->methods, op->nmethods); 403 | 404 | uint8_t reply[2]; 405 | reply[0] = 0x05; // socks5 406 | int auth = strcmp(SERVER.username, "") != 0 && strcmp(SERVER.passwd, ""); 407 | if (auth) { 408 | reply[1] = 0x02; 409 | tunnel->state = auth_state; 410 | } else { 411 | reply[1] = 0x00; 412 | tunnel->state = request_state; 413 | } 414 | *nreaded = 0; 415 | return tunnel_write_client(tunnel, reply, sizeof(reply)); 416 | } else return 0; 417 | 418 | return 0; 419 | } 420 | 421 | // |VER(1)|ULEN(1)|UNAME(1-255)|PLEN(1)|PASSWD(1-255)| 422 | static int tunnel_auth_handle(tunnel_t* tunnel) 423 | { 424 | buff_t *buff = tunnel->client_sock->read_buff; 425 | auth_protocol_t *ap = &tunnel->ap; 426 | size_t *nreaded = &tunnel->read_count; 427 | size_t nheader = sizeof(ap->ver) + sizeof(ap->ulen); 428 | size_t nplen = sizeof(ap->plen); 429 | 430 | if (*nreaded == 0) goto header; 431 | else if(*nreaded == nheader) goto uname; 432 | else if(*nreaded == nheader + ap->ulen) goto plen; 433 | else if (*nreaded == nheader + ap->ulen + nplen) goto passwd; 434 | else assert(0); 435 | 436 | header: 437 | // VER(1)|ULEN(1) 438 | if (buff_readable(buff) >= nheader) { 439 | buff_read(buff, &ap->ver, sizeof(ap->ver)); 440 | buff_read(buff, &ap->ulen, sizeof(ap->ulen)); 441 | if (ap->ulen > MAX_UNAME_LEN) return -1; 442 | 443 | *nreaded += nheader; 444 | } else return 0; 445 | 446 | uname: 447 | // UNAME(1-255) 448 | if (buff_readable(buff) >= ap->ulen) { 449 | buff_read(buff, ap->uname, ap->ulen); 450 | *nreaded += ap->ulen; 451 | } else return 0; 452 | 453 | plen: 454 | // PLEN(1) 455 | if (buff_readable(buff) >= nplen) { 456 | buff_read(buff, &ap->plen, nplen); 457 | if (ap->plen > MAX_PASSWD_LEN) return -1; 458 | *nreaded += nplen; 459 | } else return 0; 460 | 461 | passwd: 462 | // PASSWD(1-255) 463 | if (buff_readable(buff) >= ap->plen) { 464 | buff_read(buff, ap->passwd, ap->plen); 465 | if (strcmp(ap->uname, SERVER.username) != 0 || strcmp(ap->passwd, SERVER.passwd) != 0) return -1; 466 | 467 | uint8_t reply[2]; 468 | reply[0] = ap->ver; // subversion 469 | reply[1] = 0x00; // success 470 | 471 | if (tunnel_write_client(tunnel, reply, sizeof(reply)) < 0) return -1; 472 | 473 | tunnel->state = request_state; 474 | *nreaded = 0; 475 | } else return 0; 476 | 477 | return 0; 478 | } 479 | 480 | // |VER(1)|REP(1)|RSV(1)|ATYP(1))|BIND.ADDR(variable)|BIND.PORT(2)| 481 | static int tunnel_notify_connected(tunnel_t *tunnel) 482 | { 483 | sockaddr_t sa; 484 | socklen_t len = sizeof(sa); 485 | uint8_t header[4]; 486 | header[0] = 0x05; // socks5 487 | header[1] = 0x00; // success 488 | header[2] = 0x00; 489 | 490 | if (getsockname(tunnel->remote_sock->fd, &sa, &len) < 0) return -1; 491 | 492 | if (sa.sa_family == AF_INET) { 493 | header[3] = 0x01; //IPV4 494 | if (tunnel_write_client(tunnel, header, sizeof(header)) < 0) return -1; 495 | 496 | sockaddr_in_t *sa_in = (sockaddr_in_t*)&sa; 497 | if (tunnel_write_client(tunnel, &sa_in->sin_addr, sizeof(sa_in->sin_addr)) < 0) return -1; 498 | if (tunnel_write_client(tunnel, &sa_in->sin_port, sizeof(sa_in->sin_port)) < 0) return -1; 499 | } else if (sa.sa_family == AF_INET6) { 500 | header[3] = 0x04; //IPV6 501 | tunnel_write_client(tunnel, header, sizeof(header)); 502 | 503 | sockaddr_in6_t *sa_in6 = (sockaddr_in6_t*)&sa; 504 | tunnel_write_client(tunnel, &sa_in6->sin6_addr, sizeof(sa_in6->sin6_addr)); 505 | tunnel_write_client(tunnel, &sa_in6->sin6_port, sizeof(sa_in6->sin6_port)); 506 | } else { 507 | LOG("tunnel_notify_connected,unexpected family=%d", sa.sa_family); 508 | return -1; 509 | } 510 | 511 | return 0; 512 | } 513 | 514 | static int tunnel_connect_to_remote(tunnel_t *tunnel) 515 | { 516 | uint8_t atyp = tunnel->rp.atyp; 517 | char *addr; 518 | char ip[64]; 519 | char port[16]; 520 | 521 | snprintf(port, sizeof(port),"%d", ntohs(tunnel->rp.port)); 522 | switch(atyp) { 523 | case 0x01: // ipv4 524 | inet_ntop(AF_INET, tunnel->rp.addr, ip, sizeof(ip)); 525 | addr = ip; 526 | break; 527 | case 0x04: // ipv6 528 | inet_ntop(AF_INET6, tunnel->rp.addr, ip, sizeof(ip)); 529 | addr = ip; 530 | break; 531 | case 0x03: // domain 532 | addr = tunnel->rp.addr; 533 | break; 534 | default: 535 | assert(0); 536 | break; 537 | } 538 | 539 | addrinfo_t ai_hint; 540 | memset(&ai_hint, 0, sizeof(ai_hint)); 541 | 542 | ai_hint.ai_family = AF_UNSPEC; 543 | ai_hint.ai_socktype = SOCK_STREAM; 544 | ai_hint.ai_protocol = IPPROTO_TCP; 545 | 546 | addrinfo_t *ai_list; 547 | addrinfo_t *ai_ptr; 548 | 549 | // TODO: getaddrinfo is a block function, try doing it in thread 550 | if (getaddrinfo(addr, port, &ai_hint, &ai_list) != 0) { 551 | LOG("getaddrinfo failed,addr=%s,port=%s,error=%s", addr, port, gai_strerror(errno)); 552 | return -1; 553 | } 554 | 555 | int newfd = -1; 556 | int status; 557 | for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) { 558 | newfd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); 559 | if (newfd < 0) continue; 560 | sock_nonblocking(newfd); 561 | sock_keepalive(newfd); 562 | 563 | if ((status = connect(newfd, ai_ptr->ai_addr, ai_ptr->ai_addrlen)) != 0 && errno != EINPROGRESS) { 564 | close(newfd); 565 | newfd = -1; 566 | continue; 567 | } 568 | 569 | break; 570 | } 571 | freeaddrinfo(ai_list); 572 | 573 | if (newfd < 0) return -1; 574 | 575 | sock_t *sock = sock_create(newfd, sock_connecting, 0, tunnel); 576 | if (sock == NULL) { 577 | close(newfd); 578 | return -1; 579 | } 580 | tunnel->remote_sock = sock; 581 | 582 | epoll_add(sock); 583 | epoll_modify(sock, 1, 1); 584 | 585 | if (status == 0) { 586 | tunnel->state = connected_state; 587 | sock->state = sock_connected; 588 | return tunnel_notify_connected(tunnel); 589 | } else { 590 | tunnel->state = connecting_state; 591 | sock->state = sock_connecting; 592 | } 593 | 594 | return 0; 595 | } 596 | 597 | // |VER(1)|CMD(1))|RSV(1)|ATYP(1)|DST.ADDR(variable)|DST.PORT(2)| 598 | #define NIPV4 4 599 | #define NIPV6 16 600 | 601 | static int tunnel_request_handle(tunnel_t *tunnel) 602 | { 603 | buff_t *buff = tunnel->client_sock->read_buff; 604 | request_protocol_t *rp = &tunnel->rp; 605 | size_t *nreaded = &tunnel->read_count; 606 | size_t nheader = sizeof(rp->ver) + sizeof(rp->cmd) + sizeof(rp->rsv) + sizeof(rp->atyp); 607 | size_t ndomainlen = sizeof(rp->domainlen); 608 | size_t nport = sizeof(rp->port); 609 | 610 | if (*nreaded == 0) goto header; 611 | else if(*nreaded == nheader) goto addr; 612 | else if (*nreaded == nheader + ndomainlen) goto domain; 613 | else assert(0); 614 | 615 | header: 616 | // VER(1)|CMD(1))|RSV(1)|ATYP(1) 617 | if (buff_readable(buff) >= nheader) { 618 | buff_read(buff, &rp->ver, sizeof(rp->ver)); 619 | if (rp->ver != 0x05) return -1; 620 | 621 | buff_read(buff, &rp->cmd, sizeof(rp->cmd)); 622 | switch (rp->cmd) { 623 | case 0x01: // CONNECT 624 | break; 625 | case 0x02: // TODO implement BIND 626 | case 0x03: // TODO implement ASSOCIATE 627 | default: 628 | LOG("tunnel_request_handle,CMD not support,cmd=%d", rp->cmd); 629 | return -1; 630 | } 631 | 632 | buff_read(buff, &rp->rsv, sizeof(rp->rsv)); 633 | buff_read(buff, &rp->atyp, sizeof(rp->atyp)); 634 | *nreaded += nheader; 635 | } else return 0; 636 | 637 | addr: 638 | switch (rp->atyp) { 639 | case 0x01: // IPV4 640 | // DST.ADDR(variable)|DST.PORT(2) 641 | if (buff_readable(buff) >= NIPV4 + nport) { 642 | buff_read(buff, rp->addr, NIPV4); 643 | buff_read(buff, &rp->port, nport); 644 | } else return 0; 645 | break; 646 | case 0x04: // IPV6 647 | // DST.ADDR(variable)|DST.PORT(2) 648 | if (buff_readable(buff) >= NIPV6 + nport) { 649 | buff_read(buff, rp->addr, NIPV6); 650 | buff_read(buff, &rp->port, nport); 651 | } else return 0; 652 | break; 653 | case 0x03: // DOMAIN 654 | { 655 | // DST.ADDR[0](1) 656 | if (buff_readable(buff) >= ndomainlen) { 657 | buff_read(buff, &rp->domainlen, ndomainlen); 658 | *nreaded += ndomainlen; 659 | } else return 0; 660 | 661 | domain: 662 | // DST.ADDR[1](DST.ADDR[0])|DST.PORT(2) 663 | if (buff_readable(buff) >= rp->domainlen + nport) { 664 | buff_read(buff, rp->addr, rp->domainlen); 665 | buff_read(buff, &rp->port, nport); 666 | } else return 0; 667 | } 668 | break; 669 | default: 670 | return -1; 671 | } 672 | 673 | *nreaded = 0; 674 | return tunnel_connect_to_remote(tunnel); 675 | } 676 | 677 | static int tunnel_connecting_handle(tunnel_t *tunnel) 678 | { 679 | int error; 680 | socklen_t len = sizeof(error); 681 | int code = getsockopt(tunnel->remote_sock->fd, SOL_SOCKET, SO_ERROR, &error, &len); 682 | /* 683 | * If error occur, Solairs return -1 and set error to errno. 684 | * Berkeley return 0 but not set errno. 685 | */ 686 | if (code < 0 || error) { 687 | if (error) errno = error; 688 | return -1; 689 | } 690 | 691 | tunnel->state = connected_state; 692 | tunnel->remote_sock->state = sock_connected; 693 | return tunnel_notify_connected(tunnel); 694 | } 695 | 696 | /* 697 | * For data forward: 698 | * If client readable, append client_read_buff to remote_write_buff 699 | * Else, append remote_read_buff to client_write_buff 700 | */ 701 | static int tunnel_connected_handle(tunnel_t *tunnel, int client) 702 | { 703 | if (client) { 704 | if (tunnel->remote_sock == NULL) return -1; 705 | 706 | if (buff_concat(tunnel->remote_sock->write_buff, tunnel->client_sock->read_buff) < 0) return -1; 707 | buff_clear(tunnel->client_sock->read_buff); 708 | epoll_modify(tunnel->remote_sock, 1, 1); 709 | } else { 710 | if (tunnel->client_sock == NULL) return -1; 711 | 712 | if (buff_concat(tunnel->client_sock->write_buff, tunnel->remote_sock->read_buff) < 0) return -1; 713 | buff_clear(tunnel->remote_sock->read_buff); 714 | epoll_modify(tunnel->client_sock, 1, 1); 715 | } 716 | return 0; 717 | } 718 | 719 | static int tunnel_write_client(tunnel_t *tunnel, void *src, size_t size) 720 | { 721 | if (tunnel->client_sock == NULL) return -1; 722 | 723 | if (buff_write(tunnel->client_sock->write_buff, src, size) < 0) return -1; 724 | 725 | epoll_modify(tunnel->client_sock, 1, 1); 726 | return 0; 727 | } 728 | 729 | static void accept_handle() 730 | { 731 | int newfd; 732 | if ((newfd = accept(SERVER.listenfd, NULL, NULL)) < 0) { 733 | LOG("accept_handle failed,listenfd=%d,err=%s", SERVER.listenfd, strerror(errno)); 734 | return; 735 | } 736 | 737 | tunnel_create(newfd); 738 | } 739 | 740 | static void sigign() 741 | { 742 | struct sigaction sa; 743 | sa.sa_handler = SIG_IGN; 744 | sa.sa_flags = 0; 745 | sigemptyset(&sa.sa_mask); 746 | sigaction(SIGPIPE, &sa, 0); 747 | } 748 | 749 | static int server_start() 750 | { 751 | epoll_event_t events[MAX_EPOLL_EVENTS]; 752 | for(;;) { 753 | int n = epoll_wait(SERVER.epollfd, events, MAX_EPOLL_EVENTS, -1); 754 | if (n < 0 && errno != EINTR) { 755 | LOG("epoll_wait failed,error=%s", strerror(errno)); 756 | return -1; 757 | } 758 | 759 | for (int i = 0; i < n; ++i) { 760 | void *cur_ud = events[i].data.ptr; 761 | int cur_fd = *(int*)cur_ud; 762 | int cur_events = events[i].events; 763 | if (cur_events & EPOLLIN) { 764 | if (cur_fd == SERVER.listenfd) { 765 | accept_handle(); 766 | } else { 767 | tunnel_read_handle(cur_fd, cur_ud); 768 | } 769 | } else if(cur_events & EPOLLOUT) { 770 | tunnel_write_handle(cur_fd, cur_ud); 771 | } else { 772 | LOG("unexpected epoll events"); 773 | } 774 | } 775 | } 776 | 777 | return 0; 778 | } 779 | 780 | static int server_init(char *host, char *port, char *username, char *passwd) 781 | { 782 | addrinfo_t ai_hint; 783 | memset(&ai_hint, 0, sizeof(ai_hint)); 784 | 785 | ai_hint.ai_family = AF_UNSPEC; 786 | ai_hint.ai_socktype = SOCK_STREAM; 787 | ai_hint.ai_protocol = IPPROTO_TCP; 788 | 789 | addrinfo_t *ai_list; 790 | addrinfo_t *ai_ptr; 791 | 792 | if (getaddrinfo(host, port, &ai_hint, &ai_list) != 0) { 793 | LOG("init_server,getaddrinfo failed error=%s", gai_strerror(errno)); 794 | return -1; 795 | } 796 | 797 | int listenfd = -1; 798 | for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) { 799 | listenfd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); 800 | if (listenfd < 0) continue; 801 | sock_nonblocking(listenfd); 802 | 803 | break; 804 | } 805 | 806 | if (listenfd < 0) { 807 | LOG("init_server,listenfd create failed errno=%s", strerror(errno)); 808 | return -1; 809 | } 810 | 811 | int reuse = 1; 812 | setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void*)&reuse, sizeof(reuse)); 813 | 814 | if (bind(listenfd, ai_ptr->ai_addr, ai_ptr->ai_addrlen)) { 815 | LOG("bind failed, errno=%s", strerror(errno)); 816 | return -1; 817 | } 818 | freeaddrinfo(ai_list); 819 | 820 | if (listen(listenfd, BLACKLOG) != 0) { 821 | LOG("listen failed, errno=%s", strerror(errno)); 822 | return -1; 823 | } 824 | 825 | int epollfd = -1; 826 | if ((epollfd = epoll_create(1024)) < 0) { 827 | LOG("epoll_create, errno=%s", strerror(errno)); 828 | return -1; 829 | } 830 | 831 | SERVER.epollfd = epollfd; 832 | SERVER.listenfd = listenfd; 833 | 834 | epoll_event_t event; 835 | event.events = EPOLLIN; 836 | event.data.ptr = &SERVER; 837 | epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event); 838 | 839 | snprintf(SERVER.username, sizeof(SERVER.username), "%s", username); 840 | snprintf(SERVER.passwd, sizeof(SERVER.passwd), "%s", passwd); 841 | 842 | return 0; 843 | } 844 | 845 | static void usage() 846 | { 847 | fprintf(stderr, 848 | "Usage:\n" 849 | "-a : ip address\n" 850 | "-p : port \n" 851 | "-u : username\n" 852 | "-k : password\n" 853 | ); 854 | } 855 | 856 | int main(int n, char **args) 857 | { 858 | sigign(); 859 | 860 | char option; 861 | char addr[64] = ""; 862 | char port[16] = ""; 863 | char username[255] = ""; 864 | char passwd[255] = ""; 865 | 866 | while((option = getopt(n, args, "a:p:u:k:")) > 0) { 867 | switch(option) { 868 | case 'a': 869 | strncpy(addr, optarg, sizeof(addr)); 870 | break; 871 | case 'p': 872 | strncpy(port, optarg, sizeof(port)); 873 | break; 874 | case 'u': 875 | strncpy(username, optarg, sizeof(username)); 876 | break; 877 | case 'k': 878 | strncpy(passwd, optarg, sizeof(passwd)); 879 | break; 880 | default: 881 | usage(); 882 | break; 883 | } 884 | } 885 | 886 | if (strcmp(port, "") == 0 || strcmp(addr, "") == 0) { 887 | usage(); 888 | return -1; 889 | } 890 | 891 | if (server_init(addr, port, username, passwd) < 0) return -1; 892 | if (server_start() < 0) return -1; 893 | 894 | return 0; 895 | } 896 | --------------------------------------------------------------------------------