├── .travis.yml ├── LICENSE ├── Makefile ├── main.c ├── readme.md └── test.sh /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: false 3 | script: 4 | - make 5 | - make test 6 | 7 | after_success: 8 | - echo SUCCESS 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-c -pthread -Wno-unused-result -g -std=gnu99 -Wall 3 | LDFLAGS=-pthread 4 | SOURCES=main.c 5 | OBJECTS=$(SOURCES:.c=.o) 6 | EXECUTABLE=proxy 7 | 8 | all: $(EXECUTABLE) 9 | 10 | $(EXECUTABLE): $(OBJECTS) 11 | $(CC) $(LDFLAGS) $(OBJECTS) -o $@ 12 | 13 | .c.o: 14 | $(CC) $(CFLAGS) $< -o $@ 15 | 16 | clean: 17 | rm -rf $(OBJECTS) $(EXECUTABLE) 18 | 19 | test: 20 | @chmod +x test.sh 21 | @bash ./test.sh 22 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 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 | #include 18 | #include 19 | #include 20 | 21 | #define BUFSIZE 65536 22 | #define IPSIZE 4 23 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) 24 | #define ARRAY_INIT {0} 25 | 26 | unsigned short int port = 1080; 27 | int daemon_mode = 0; 28 | int auth_type; 29 | char *arg_username; 30 | char *arg_password; 31 | FILE *log_file; 32 | pthread_mutex_t lock; 33 | 34 | enum socks { 35 | RESERVED = 0x00, 36 | VERSION4 = 0x04, 37 | VERSION5 = 0x05 38 | }; 39 | 40 | enum socks_auth_methods { 41 | NOAUTH = 0x00, 42 | USERPASS = 0x02, 43 | NOMETHOD = 0xff 44 | }; 45 | 46 | enum socks_auth_userpass { 47 | AUTH_OK = 0x00, 48 | AUTH_VERSION = 0x01, 49 | AUTH_FAIL = 0xff 50 | }; 51 | 52 | enum socks_command { 53 | CONNECT = 0x01 54 | }; 55 | 56 | enum socks_command_type { 57 | IP = 0x01, 58 | DOMAIN = 0x03 59 | }; 60 | 61 | enum socks_status { 62 | OK = 0x00, 63 | FAILED = 0x05 64 | }; 65 | 66 | void log_message(const char *message, ...) 67 | { 68 | if (daemon_mode) { 69 | return; 70 | } 71 | 72 | char vbuffer[255]; 73 | va_list args; 74 | va_start(args, message); 75 | vsnprintf(vbuffer, ARRAY_SIZE(vbuffer), message, args); 76 | va_end(args); 77 | 78 | time_t now; 79 | time(&now); 80 | char *date = ctime(&now); 81 | date[strlen(date) - 1] = '\0'; 82 | 83 | pthread_t self = pthread_self(); 84 | 85 | if (errno != 0) { 86 | pthread_mutex_lock(&lock); 87 | fprintf(log_file, "[%s][%lu] Critical: %s - %s\n", date, self, 88 | vbuffer, strerror(errno)); 89 | errno = 0; 90 | pthread_mutex_unlock(&lock); 91 | } else { 92 | fprintf(log_file, "[%s][%lu] Info: %s\n", date, self, vbuffer); 93 | } 94 | fflush(log_file); 95 | } 96 | 97 | int readn(int fd, void *buf, int n) 98 | { 99 | int nread, left = n; 100 | while (left > 0) { 101 | if ((nread = read(fd, buf, left)) == -1) { 102 | if (errno == EINTR || errno == EAGAIN) { 103 | continue; 104 | } 105 | } else { 106 | if (nread == 0) { 107 | return 0; 108 | } else { 109 | left -= nread; 110 | buf += nread; 111 | } 112 | } 113 | } 114 | return n; 115 | } 116 | 117 | int writen(int fd, void *buf, int n) 118 | { 119 | int nwrite, left = n; 120 | while (left > 0) { 121 | if ((nwrite = write(fd, buf, left)) == -1) { 122 | if (errno == EINTR || errno == EAGAIN) { 123 | continue; 124 | } 125 | } else { 126 | if (nwrite == n) { 127 | return 0; 128 | } else { 129 | left -= nwrite; 130 | buf += nwrite; 131 | } 132 | } 133 | } 134 | return n; 135 | } 136 | 137 | void app_thread_exit(int ret, int fd) 138 | { 139 | close(fd); 140 | pthread_exit((void *)&ret); 141 | } 142 | 143 | int app_connect(int type, void *buf, unsigned short int portnum) 144 | { 145 | int fd; 146 | struct sockaddr_in remote; 147 | char address[16]; 148 | 149 | memset(address, 0, ARRAY_SIZE(address)); 150 | 151 | if (type == IP) { 152 | char *ip = (char *)buf; 153 | snprintf(address, ARRAY_SIZE(address), "%hhu.%hhu.%hhu.%hhu", 154 | ip[0], ip[1], ip[2], ip[3]); 155 | memset(&remote, 0, sizeof(remote)); 156 | remote.sin_family = AF_INET; 157 | remote.sin_addr.s_addr = inet_addr(address); 158 | remote.sin_port = htons(portnum); 159 | 160 | fd = socket(AF_INET, SOCK_STREAM, 0); 161 | if (connect(fd, (struct sockaddr *)&remote, sizeof(remote)) < 0) { 162 | log_message("connect() in app_connect"); 163 | close(fd); 164 | return -1; 165 | } 166 | 167 | return fd; 168 | } else if (type == DOMAIN) { 169 | char portaddr[6]; 170 | struct addrinfo *res; 171 | snprintf(portaddr, ARRAY_SIZE(portaddr), "%d", portnum); 172 | log_message("getaddrinfo: %s %s", (char *)buf, portaddr); 173 | int ret = getaddrinfo((char *)buf, portaddr, NULL, &res); 174 | if (ret == EAI_NODATA) { 175 | return -1; 176 | } else if (ret == 0) { 177 | struct addrinfo *r; 178 | for (r = res; r != NULL; r = r->ai_next) { 179 | fd = socket(r->ai_family, r->ai_socktype, 180 | r->ai_protocol); 181 | if (fd == -1) { 182 | continue; 183 | } 184 | ret = connect(fd, r->ai_addr, r->ai_addrlen); 185 | if (ret == 0) { 186 | freeaddrinfo(res); 187 | return fd; 188 | } else { 189 | close(fd); 190 | } 191 | } 192 | } 193 | freeaddrinfo(res); 194 | return -1; 195 | } 196 | 197 | return -1; 198 | } 199 | 200 | int socks_invitation(int fd, int *version) 201 | { 202 | char init[2]; 203 | int nread = readn(fd, (void *)init, ARRAY_SIZE(init)); 204 | if (nread == 2 && init[0] != VERSION5 && init[0] != VERSION4) { 205 | log_message("They send us %hhX %hhX", init[0], init[1]); 206 | log_message("Incompatible version!"); 207 | app_thread_exit(0, fd); 208 | } 209 | log_message("Initial %hhX %hhX", init[0], init[1]); 210 | *version = init[0]; 211 | return init[1]; 212 | } 213 | 214 | char *socks5_auth_get_user(int fd) 215 | { 216 | unsigned char size; 217 | readn(fd, (void *)&size, sizeof(size)); 218 | 219 | char *user = (char *)malloc(sizeof(char) * size + 1); 220 | readn(fd, (void *)user, (int)size); 221 | user[size] = 0; 222 | 223 | return user; 224 | } 225 | 226 | char *socks5_auth_get_pass(int fd) 227 | { 228 | unsigned char size; 229 | readn(fd, (void *)&size, sizeof(size)); 230 | 231 | char *pass = (char *)malloc(sizeof(char) * size + 1); 232 | readn(fd, (void *)pass, (int)size); 233 | pass[size] = 0; 234 | 235 | return pass; 236 | } 237 | 238 | int socks5_auth_userpass(int fd) 239 | { 240 | char answer[2] = { VERSION5, USERPASS }; 241 | writen(fd, (void *)answer, ARRAY_SIZE(answer)); 242 | char resp; 243 | readn(fd, (void *)&resp, sizeof(resp)); 244 | log_message("auth %hhX", resp); 245 | char *username = socks5_auth_get_user(fd); 246 | char *password = socks5_auth_get_pass(fd); 247 | log_message("l: %s p: %s", username, password); 248 | if (strcmp(arg_username, username) == 0 249 | && strcmp(arg_password, password) == 0) { 250 | char answer[2] = { AUTH_VERSION, AUTH_OK }; 251 | writen(fd, (void *)answer, ARRAY_SIZE(answer)); 252 | free(username); 253 | free(password); 254 | return 0; 255 | } else { 256 | char answer[2] = { AUTH_VERSION, AUTH_FAIL }; 257 | writen(fd, (void *)answer, ARRAY_SIZE(answer)); 258 | free(username); 259 | free(password); 260 | return 1; 261 | } 262 | } 263 | 264 | int socks5_auth_noauth(int fd) 265 | { 266 | char answer[2] = { VERSION5, NOAUTH }; 267 | writen(fd, (void *)answer, ARRAY_SIZE(answer)); 268 | return 0; 269 | } 270 | 271 | void socks5_auth_notsupported(int fd) 272 | { 273 | char answer[2] = { VERSION5, NOMETHOD }; 274 | writen(fd, (void *)answer, ARRAY_SIZE(answer)); 275 | } 276 | 277 | void socks5_auth(int fd, int methods_count) 278 | { 279 | int supported = 0; 280 | int num = methods_count; 281 | for (int i = 0; i < num; i++) { 282 | char type; 283 | readn(fd, (void *)&type, 1); 284 | log_message("Method AUTH %hhX", type); 285 | if (type == auth_type) { 286 | supported = 1; 287 | } 288 | } 289 | if (supported == 0) { 290 | socks5_auth_notsupported(fd); 291 | app_thread_exit(1, fd); 292 | } 293 | int ret = 0; 294 | switch (auth_type) { 295 | case NOAUTH: 296 | ret = socks5_auth_noauth(fd); 297 | break; 298 | case USERPASS: 299 | ret = socks5_auth_userpass(fd); 300 | break; 301 | } 302 | if (ret == 0) { 303 | return; 304 | } else { 305 | app_thread_exit(1, fd); 306 | } 307 | } 308 | 309 | int socks5_command(int fd) 310 | { 311 | char command[4]; 312 | readn(fd, (void *)command, ARRAY_SIZE(command)); 313 | log_message("Command %hhX %hhX %hhX %hhX", command[0], command[1], 314 | command[2], command[3]); 315 | return command[3]; 316 | } 317 | 318 | unsigned short int socks_read_port(int fd) 319 | { 320 | unsigned short int p; 321 | readn(fd, (void *)&p, sizeof(p)); 322 | log_message("Port %hu", ntohs(p)); 323 | return p; 324 | } 325 | 326 | char *socks_ip_read(int fd) 327 | { 328 | char *ip = (char *)malloc(sizeof(char) * IPSIZE); 329 | readn(fd, (void *)ip, IPSIZE); 330 | log_message("IP %hhu.%hhu.%hhu.%hhu", ip[0], ip[1], ip[2], ip[3]); 331 | return ip; 332 | } 333 | 334 | void socks5_ip_send_response(int fd, char *ip, unsigned short int port) 335 | { 336 | char response[4] = { VERSION5, OK, RESERVED, IP }; 337 | writen(fd, (void *)response, ARRAY_SIZE(response)); 338 | writen(fd, (void *)ip, IPSIZE); 339 | writen(fd, (void *)&port, sizeof(port)); 340 | } 341 | 342 | char *socks5_domain_read(int fd, unsigned char *size) 343 | { 344 | unsigned char s; 345 | readn(fd, (void *)&s, sizeof(s)); 346 | char *address = (char *)malloc((sizeof(char) * s) + 1); 347 | readn(fd, (void *)address, (int)s); 348 | address[s] = 0; 349 | log_message("Address %s", address); 350 | *size = s; 351 | return address; 352 | } 353 | 354 | void socks5_domain_send_response(int fd, char *domain, unsigned char size, 355 | unsigned short int port) 356 | { 357 | char response[4] = { VERSION5, OK, RESERVED, DOMAIN }; 358 | writen(fd, (void *)response, ARRAY_SIZE(response)); 359 | writen(fd, (void *)&size, sizeof(size)); 360 | writen(fd, (void *)domain, size * sizeof(char)); 361 | writen(fd, (void *)&port, sizeof(port)); 362 | } 363 | 364 | int socks4_is_4a(char *ip) 365 | { 366 | return (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] != 0); 367 | } 368 | 369 | int socks4_read_nstring(int fd, char *buf, int size) 370 | { 371 | char sym = 0; 372 | int nread = 0; 373 | int i = 0; 374 | 375 | while (i < size) { 376 | nread = recv(fd, &sym, sizeof(char), 0); 377 | 378 | if (nread <= 0) { 379 | break; 380 | } else { 381 | buf[i] = sym; 382 | i++; 383 | } 384 | 385 | if (sym == 0) { 386 | break; 387 | } 388 | } 389 | 390 | return i; 391 | } 392 | 393 | void socks4_send_response(int fd, int status) 394 | { 395 | char resp[8] = {0x00, (char)status, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 396 | writen(fd, (void *)resp, ARRAY_SIZE(resp)); 397 | } 398 | 399 | void app_socket_pipe(int fd0, int fd1) 400 | { 401 | int maxfd, ret; 402 | fd_set rd_set; 403 | size_t nread; 404 | char buffer_r[BUFSIZE]; 405 | 406 | log_message("Connecting two sockets"); 407 | 408 | maxfd = (fd0 > fd1) ? fd0 : fd1; 409 | while (1) { 410 | FD_ZERO(&rd_set); 411 | FD_SET(fd0, &rd_set); 412 | FD_SET(fd1, &rd_set); 413 | ret = select(maxfd + 1, &rd_set, NULL, NULL, NULL); 414 | 415 | if (ret < 0 && errno == EINTR) { 416 | continue; 417 | } 418 | 419 | if (FD_ISSET(fd0, &rd_set)) { 420 | nread = recv(fd0, buffer_r, BUFSIZE, 0); 421 | if (nread <= 0) 422 | break; 423 | send(fd1, (const void *)buffer_r, nread, 0); 424 | } 425 | 426 | if (FD_ISSET(fd1, &rd_set)) { 427 | nread = recv(fd1, buffer_r, BUFSIZE, 0); 428 | if (nread <= 0) 429 | break; 430 | send(fd0, (const void *)buffer_r, nread, 0); 431 | } 432 | } 433 | } 434 | 435 | void *app_thread_process(void *fd) 436 | { 437 | int net_fd = *(int *)fd; 438 | int version = 0; 439 | int inet_fd = -1; 440 | char methods = socks_invitation(net_fd, &version); 441 | 442 | switch (version) { 443 | case VERSION5: { 444 | socks5_auth(net_fd, methods); 445 | int command = socks5_command(net_fd); 446 | 447 | if (command == IP) { 448 | char *ip = socks_ip_read(net_fd); 449 | unsigned short int p = socks_read_port(net_fd); 450 | 451 | inet_fd = app_connect(IP, (void *)ip, ntohs(p)); 452 | if (inet_fd == -1) { 453 | app_thread_exit(1, net_fd); 454 | } 455 | socks5_ip_send_response(net_fd, ip, p); 456 | free(ip); 457 | break; 458 | } else if (command == DOMAIN) { 459 | unsigned char size; 460 | char *address = socks5_domain_read(net_fd, &size); 461 | unsigned short int p = socks_read_port(net_fd); 462 | 463 | inet_fd = app_connect(DOMAIN, (void *)address, ntohs(p)); 464 | if (inet_fd == -1) { 465 | app_thread_exit(1, net_fd); 466 | } 467 | socks5_domain_send_response(net_fd, address, size, p); 468 | free(address); 469 | break; 470 | } else { 471 | app_thread_exit(1, net_fd); 472 | } 473 | } 474 | case VERSION4: { 475 | if (methods == 1) { 476 | char ident[255]; 477 | unsigned short int p = socks_read_port(net_fd); 478 | char *ip = socks_ip_read(net_fd); 479 | socks4_read_nstring(net_fd, ident, sizeof(ident)); 480 | 481 | if (socks4_is_4a(ip)) { 482 | char domain[255]; 483 | socks4_read_nstring(net_fd, domain, sizeof(domain)); 484 | log_message("Socks4A: ident:%s; domain:%s;", ident, domain); 485 | inet_fd = app_connect(DOMAIN, (void *)domain, ntohs(p)); 486 | } else { 487 | log_message("Socks4: connect by ip & port"); 488 | inet_fd = app_connect(IP, (void *)ip, ntohs(p)); 489 | } 490 | 491 | if (inet_fd != -1) { 492 | socks4_send_response(net_fd, 0x5a); 493 | } else { 494 | socks4_send_response(net_fd, 0x5b); 495 | free(ip); 496 | app_thread_exit(1, net_fd); 497 | } 498 | 499 | free(ip); 500 | } else { 501 | log_message("Unsupported mode"); 502 | } 503 | break; 504 | } 505 | } 506 | 507 | app_socket_pipe(inet_fd, net_fd); 508 | close(inet_fd); 509 | app_thread_exit(0, net_fd); 510 | 511 | return NULL; 512 | } 513 | 514 | int app_loop() 515 | { 516 | int sock_fd, net_fd; 517 | int optval = 1; 518 | struct sockaddr_in local, remote; 519 | socklen_t remotelen; 520 | if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 521 | log_message("socket()"); 522 | exit(1); 523 | } 524 | 525 | if (setsockopt 526 | (sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, 527 | sizeof(optval)) < 0) { 528 | log_message("setsockopt()"); 529 | exit(1); 530 | } 531 | 532 | memset(&local, 0, sizeof(local)); 533 | local.sin_family = AF_INET; 534 | local.sin_addr.s_addr = htonl(INADDR_ANY); 535 | local.sin_port = htons(port); 536 | 537 | if (bind(sock_fd, (struct sockaddr *)&local, sizeof(local)) < 0) { 538 | log_message("bind()"); 539 | exit(1); 540 | } 541 | 542 | if (listen(sock_fd, 25) < 0) { 543 | log_message("listen()"); 544 | exit(1); 545 | } 546 | 547 | remotelen = sizeof(remote); 548 | memset(&remote, 0, sizeof(remote)); 549 | 550 | log_message("Listening port %d...", port); 551 | 552 | pthread_t worker; 553 | while (1) { 554 | if ((net_fd = 555 | accept(sock_fd, (struct sockaddr *)&remote, 556 | &remotelen)) < 0) { 557 | log_message("accept()"); 558 | exit(1); 559 | } 560 | int one = 1; 561 | setsockopt(sock_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)); 562 | if (pthread_create 563 | (&worker, NULL, &app_thread_process, 564 | (void *)&net_fd) == 0) { 565 | pthread_detach(worker); 566 | } else { 567 | log_message("pthread_create()"); 568 | } 569 | } 570 | } 571 | 572 | void daemonize() 573 | { 574 | pid_t pid; 575 | int x; 576 | 577 | pid = fork(); 578 | 579 | if (pid < 0) { 580 | exit(EXIT_FAILURE); 581 | } 582 | 583 | if (pid > 0) { 584 | exit(EXIT_SUCCESS); 585 | } 586 | 587 | if (setsid() < 0) { 588 | exit(EXIT_FAILURE); 589 | } 590 | 591 | signal(SIGCHLD, SIG_IGN); 592 | signal(SIGHUP, SIG_IGN); 593 | 594 | pid = fork(); 595 | 596 | if (pid < 0) { 597 | exit(EXIT_FAILURE); 598 | } 599 | 600 | if (pid > 0) { 601 | exit(EXIT_SUCCESS); 602 | } 603 | 604 | umask(0); 605 | chdir("/"); 606 | 607 | for (x = sysconf(_SC_OPEN_MAX); x >= 0; x--) { 608 | close(x); 609 | } 610 | } 611 | 612 | void usage(char *app) 613 | { 614 | printf 615 | ("USAGE: %s [-h][-n PORT][-a AUTHTYPE][-u USERNAME][-p PASSWORD][-l LOGFILE]\n", 616 | app); 617 | printf("AUTHTYPE: 0 for NOAUTH, 2 for USERPASS\n"); 618 | printf 619 | ("By default: port is 1080, authtype is no auth, logfile is stdout\n"); 620 | exit(1); 621 | } 622 | 623 | int main(int argc, char *argv[]) 624 | { 625 | int ret; 626 | log_file = stdout; 627 | auth_type = NOAUTH; 628 | arg_username = "user"; 629 | arg_password = "pass"; 630 | pthread_mutex_init(&lock, NULL); 631 | 632 | signal(SIGPIPE, SIG_IGN); 633 | 634 | while ((ret = getopt(argc, argv, "n:u:p:l:a:hd")) != -1) { 635 | switch (ret) { 636 | case 'd':{ 637 | daemon_mode = 1; 638 | daemonize(); 639 | break; 640 | } 641 | case 'n':{ 642 | port = atoi(optarg) & 0xffff; 643 | break; 644 | } 645 | case 'u':{ 646 | arg_username = strdup(optarg); 647 | break; 648 | } 649 | case 'p':{ 650 | arg_password = strdup(optarg); 651 | break; 652 | } 653 | case 'l':{ 654 | freopen(optarg, "wa", log_file); 655 | break; 656 | } 657 | case 'a':{ 658 | auth_type = atoi(optarg); 659 | break; 660 | } 661 | case 'h': 662 | default: 663 | usage(argv[0]); 664 | } 665 | } 666 | log_message("Starting with authtype %X", auth_type); 667 | if (auth_type != NOAUTH) { 668 | log_message("Username is %s, password is %s", arg_username, 669 | arg_password); 670 | } 671 | app_loop(); 672 | return 0; 673 | } 674 | 675 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ### Socks proxy 2 | Socks proxy server written in one C file. 3 | Supports socks4, socks4a and socks5 protocols without binding and udp stuff. 4 | Can be used as example how to write your own. 5 | 6 | #### Build status and CI pipeline link 7 | [![Build Status](https://travis-ci.org/fgssfgss/socks_proxy.svg?branch=master)](https://travis-ci.org/fgssfgss/socks_proxy) 8 | 9 | #### Proto 10 | [Socks5 proto](https://tools.ietf.org/html/rfc1928) 11 | 12 | [Socks5 userpass auth proto](https://tools.ietf.org/html/rfc1929) 13 | 14 | [Socks4 proto](https://www.openssh.com/txt/socks4.protocol) 15 | 16 | [Socks4a proto](https://www.openssh.com/txt/socks4a.protocol) 17 | 18 | #### Works on 19 | Every OS which supports POSIX 20 | 21 | #### TODO 22 | 1. TCP port binding 23 | 2. UDP port binding 24 | 25 | #### Usage 26 | [-h] - *print usage* 27 | 28 | [-n PORT] - *set port to listen* 29 | 30 | [-a AUTHTYPE] - *set authtype: 0 for NOAUTH, 2 for USERPASS* 31 | 32 | [-u USERNAME] - *set username for userpass authtype* 33 | 34 | [-p PASSWORD] - *set password for userpass authtype* 35 | 36 | [-l LOGFILE] - *set file for logging output* 37 | 38 | #### Build and run 39 | No additional requirements, only compiler or crosscompiler needed 40 | 41 | make 42 | make test 43 | ./proxy 44 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | HOST=127.0.0.1 2 | PORT=1080 3 | GETIP="https://2ip.ru" 4 | PID=0 5 | SERVER_NAME="proxy" 6 | OUTLOG=log.txt 7 | PASSED=0 8 | 9 | start_server(){ 10 | echo "Starting server" 11 | "./${SERVER_NAME}" &>$OUTLOG &disown; 12 | PID=$! 13 | } 14 | 15 | stop_server() { 16 | echo "Stopping server" 17 | kill -9 $PID 18 | } 19 | 20 | fail() { 21 | stop_server 22 | exit 1 23 | } 24 | 25 | send_request_using_proxy4() { 26 | VAL_PROXY4=$(curl -s -x socks4h://$HOST:$PORT $1) 27 | } 28 | 29 | send_request_using_proxy4a() { 30 | VAL_PROXY4A=$(curl -s -x socks4ah://$HOST:$PORT $1) 31 | } 32 | 33 | send_request_using_proxy5() { 34 | VAL_PROXY5=$(curl -s -x socks5h://$HOST:$PORT $1) 35 | } 36 | 37 | send_request_without_proxy() { 38 | VAL_WPROXY=$(curl -s $1) 39 | } 40 | 41 | check_ip_test4() { 42 | echo "4: Check ip test" 43 | send_request_using_proxy4 $GETIP 44 | send_request_without_proxy $GETIP 45 | if [ "$VAL_PROXY4" != "$VAL_WPROXY" ]; then 46 | fail 47 | fi 48 | } 49 | 50 | check_ip_test4a() { 51 | echo "4a: Check ip test" 52 | send_request_using_proxy4a $GETIP 53 | send_request_without_proxy $GETIP 54 | if [ "$VAL_PROXY4A" != "$VAL_WPROXY" ]; then 55 | fail 56 | fi 57 | } 58 | 59 | check_ip_test5() { 60 | echo "5: Check ip test" 61 | send_request_using_proxy5 $GETIP 62 | send_request_without_proxy $GETIP 63 | if [ "$VAL_PROXY5" != "$VAL_WPROXY" ]; then 64 | fail 65 | fi 66 | } 67 | 68 | stability_test4() { 69 | echo "4: Stability test" 70 | for i in {1..10}; 71 | do send_request_using_proxy4 $GETIP && echo "Success"; 72 | done; 73 | } 74 | 75 | stability_test4a() { 76 | echo "4a: Stability test" 77 | for i in {1..10}; 78 | do send_request_using_proxy4a $GETIP && echo "Success"; 79 | done; 80 | } 81 | 82 | stability_test5() { 83 | echo "5: Stability test" 84 | for i in {1..10}; 85 | do send_request_using_proxy4a $GETIP && echo "Success"; 86 | done; 87 | } 88 | 89 | 90 | rm $OUTLOG 91 | start_server 92 | stability_test4 93 | stability_test4a 94 | stability_test5 95 | #check_ip_test4 96 | #check_ip_test4a 97 | #check_ip_test5 98 | stop_server 99 | 100 | echo "Server log:" 101 | cat $OUTLOG 102 | --------------------------------------------------------------------------------