Untitled
unknown
plain_text
a year ago
14 kB
1
Indexable
#define _GNU_SOURCE #include <sys/types.h> #include <stdio.h> #include <stdarg.h> #include <time.h> #include <errno.h> #include <unistd.h> #include <signal.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <sys/socket.h> #include <sys/fcntl.h> #include <sys/stat.h> #include <netdb.h> #include <sys/select.h> #include <arpa/inet.h> #include <netinet/tcp.h> #include <pthread.h> #define BUFSIZE 65536 #define IPSIZE 4 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) #define ARRAY_INIT {0} unsigned short int port = 1080; int daemon_mode = 0; int auth_type; char *arg_username; char *arg_password; FILE *log_file; pthread_mutex_t lock; enum socks { RESERVED = 0x00, VERSION4 = 0x04, VERSION5 = 0x05 }; enum socks_auth_methods { NOAUTH = 0x00, USERPASS = 0x02, NOMETHOD = 0xff }; enum socks_auth_userpass { AUTH_OK = 0x00, AUTH_VERSION = 0x01, AUTH_FAIL = 0xff }; enum socks_command { CONNECT = 0x01 }; enum socks_command_type { IP = 0x01, DOMAIN = 0x03 }; enum socks_status { OK = 0x00, FAILED = 0x05 }; void log_message(const char *message, ...) { if (daemon_mode) { return; } char vbuffer[255]; va_list args; va_start(args, message); vsnprintf(vbuffer, ARRAY_SIZE(vbuffer), message, args); va_end(args); time_t now; time(&now); char *date = ctime(&now); date[strlen(date) - 1] = '\0'; pthread_t self = pthread_self(); if (errno != 0) { pthread_mutex_lock(&lock); //fprintf(log_file, "[%s][%lu] Critical: %s - %s\n", date, self,vbuffer, strerror(errno)); errno = 0; pthread_mutex_unlock(&lock); } else { //fprintf(log_file, "[%s][%lu] Info: %s\n", date, self, vbuffer); } fflush(log_file); } int readn(int fd, void *buf, int n) { int nread, left = n; while (left > 0) { if ((nread = read(fd, buf, left)) == -1) { if (errno == EINTR || errno == EAGAIN) { continue; } } else { if (nread == 0) { return 0; } else { left -= nread; buf += nread; } } } return n; } int writen(int fd, void *buf, int n) { int nwrite, left = n; while (left > 0) { if ((nwrite = write(fd, buf, left)) == -1) { if (errno == EINTR || errno == EAGAIN) { continue; } } else { if (nwrite == n) { return 0; } else { left -= nwrite; buf += nwrite; } } } return n; } void app_thread_exit(int ret, int fd) { close(fd); pthread_exit((void *)&ret); } int app_connect(int type, void *buf, unsigned short int portnum) { int fd; struct sockaddr_in remote; char address[16]; memset(address, 0, ARRAY_SIZE(address)); if (type == IP) { char *ip = (char *)buf; snprintf(address, ARRAY_SIZE(address), "%hhu.%hhu.%hhu.%hhu", ip[0], ip[1], ip[2], ip[3]); memset(&remote, 0, sizeof(remote)); remote.sin_family = AF_INET; remote.sin_addr.s_addr = inet_addr(address); remote.sin_port = htons(portnum); fd = socket(AF_INET, SOCK_STREAM, 0); if (connect(fd, (struct sockaddr *)&remote, sizeof(remote)) < 0) { //log_message("connect() in app_connect"); close(fd); return -1; } return fd; } else if (type == DOMAIN) { char portaddr[6]; struct addrinfo *res; snprintf(portaddr, ARRAY_SIZE(portaddr), "%d", portnum); //log_message("getaddrinfo: %s %s", (char *)buf, portaddr); int ret = getaddrinfo((char *)buf, portaddr, NULL, &res); if (ret == EAI_NODATA) { return -1; } else if (ret == 0) { struct addrinfo *r; for (r = res; r != NULL; r = r->ai_next) { fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol); if (fd == -1) { continue; } ret = connect(fd, r->ai_addr, r->ai_addrlen); if (ret == 0) { freeaddrinfo(res); return fd; } else { close(fd); } } } freeaddrinfo(res); return -1; } return -1; } int socks_invitation(int fd, int *version) { char init[2]; int nread = readn(fd, (void *)init, ARRAY_SIZE(init)); if (nread == 2 && init[0] != VERSION5 && init[0] != VERSION4) { //log_message("They send us %hhX %hhX", init[0], init[1]); //log_message("Incompatible version!"); app_thread_exit(0, fd); } //log_message("Initial %hhX %hhX", init[0], init[1]); *version = init[0]; return init[1]; } char *socks5_auth_get_user(int fd) { unsigned char size; readn(fd, (void *)&size, sizeof(size)); char *user = (char *)malloc(sizeof(char) * size + 1); readn(fd, (void *)user, (int)size); user[size] = 0; return user; } char *socks5_auth_get_pass(int fd) { unsigned char size; readn(fd, (void *)&size, sizeof(size)); char *pass = (char *)malloc(sizeof(char) * size + 1); readn(fd, (void *)pass, (int)size); pass[size] = 0; return pass; } int socks5_auth_userpass(int fd) { char answer[2] = { VERSION5, USERPASS }; writen(fd, (void *)answer, ARRAY_SIZE(answer)); char resp; readn(fd, (void *)&resp, sizeof(resp)); //log_message("auth %hhX", resp); char *username = socks5_auth_get_user(fd); char *password = socks5_auth_get_pass(fd); //log_message("l: %s p: %s", username, password); if (strcmp(arg_username, username) == 0 && strcmp(arg_password, password) == 0) { char answer[2] = { AUTH_VERSION, AUTH_OK }; writen(fd, (void *)answer, ARRAY_SIZE(answer)); free(username); free(password); return 0; } else { char answer[2] = { AUTH_VERSION, AUTH_FAIL }; writen(fd, (void *)answer, ARRAY_SIZE(answer)); free(username); free(password); return 1; } } int socks5_auth_noauth(int fd) { char answer[2] = { VERSION5, NOAUTH }; writen(fd, (void *)answer, ARRAY_SIZE(answer)); return 0; } void socks5_auth_notsupported(int fd) { char answer[2] = { VERSION5, NOMETHOD }; writen(fd, (void *)answer, ARRAY_SIZE(answer)); } void socks5_auth(int fd, int methods_count) { int supported = 0; int num = methods_count; for (int i = 0; i < num; i++) { char type; readn(fd, (void *)&type, 1); //log_message("Method AUTH %hhX", type); if (type == auth_type) { supported = 1; } } if (supported == 0) { socks5_auth_notsupported(fd); app_thread_exit(1, fd); } int ret = 0; switch (auth_type) { case NOAUTH: ret = socks5_auth_noauth(fd); break; case USERPASS: ret = socks5_auth_userpass(fd); break; } if (ret == 0) { return; } else { app_thread_exit(1, fd); } } int socks5_command(int fd) { char command[4]; readn(fd, (void *)command, ARRAY_SIZE(command)); //log_message("Command %hhX %hhX %hhX %hhX", command[0], command[1],command[2], command[3]); return command[3]; } unsigned short int socks_read_port(int fd) { unsigned short int p; readn(fd, (void *)&p, sizeof(p)); //log_message("Port %hu", ntohs(p)); return p; } char *socks_ip_read(int fd) { char *ip = (char *)malloc(sizeof(char) * IPSIZE); readn(fd, (void *)ip, IPSIZE); //log_message("IP %hhu.%hhu.%hhu.%hhu", ip[0], ip[1], ip[2], ip[3]); return ip; } void socks5_ip_send_response(int fd, char *ip, unsigned short int port) { char response[4] = { VERSION5, OK, RESERVED, IP }; writen(fd, (void *)response, ARRAY_SIZE(response)); writen(fd, (void *)ip, IPSIZE); writen(fd, (void *)&port, sizeof(port)); } char *socks5_domain_read(int fd, unsigned char *size) { unsigned char s; readn(fd, (void *)&s, sizeof(s)); char *address = (char *)malloc((sizeof(char) * s) + 1); readn(fd, (void *)address, (int)s); address[s] = 0; //log_message("Address %s", address); *size = s; return address; } void socks5_domain_send_response(int fd, char *domain, unsigned char size, unsigned short int port) { char response[4] = { VERSION5, OK, RESERVED, DOMAIN }; writen(fd, (void *)response, ARRAY_SIZE(response)); writen(fd, (void *)&size, sizeof(size)); writen(fd, (void *)domain, size * sizeof(char)); writen(fd, (void *)&port, sizeof(port)); } int socks4_is_4a(char *ip) { return (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] != 0); } int socks4_read_nstring(int fd, char *buf, int size) { char sym = 0; int nread = 0; int i = 0; while (i < size) { nread = recv(fd, &sym, sizeof(char), 0); if (nread <= 0) { break; } else { buf[i] = sym; i++; } if (sym == 0) { break; } } return i; } void socks4_send_response(int fd, int status) { char resp[8] = {0x00, (char)status, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; writen(fd, (void *)resp, ARRAY_SIZE(resp)); } void app_socket_pipe(int fd0, int fd1) { int maxfd, ret; fd_set rd_set; size_t nread; char buffer_r[BUFSIZE]; //log_message("Connecting two sockets"); maxfd = (fd0 > fd1) ? fd0 : fd1; while (1) { FD_ZERO(&rd_set); FD_SET(fd0, &rd_set); FD_SET(fd1, &rd_set); ret = select(maxfd + 1, &rd_set, NULL, NULL, NULL); if (ret < 0 && errno == EINTR) { continue; } if (FD_ISSET(fd0, &rd_set)) { nread = recv(fd0, buffer_r, BUFSIZE, 0); if (nread <= 0) break; send(fd1, (const void *)buffer_r, nread, 0); } if (FD_ISSET(fd1, &rd_set)) { nread = recv(fd1, buffer_r, BUFSIZE, 0); if (nread <= 0) break; send(fd0, (const void *)buffer_r, nread, 0); } } } void *app_thread_process(void *fd) { int net_fd = *(int *)fd; int version = 0; int inet_fd = -1; char methods = socks_invitation(net_fd, &version); switch (version) { case VERSION5: { socks5_auth(net_fd, methods); int command = socks5_command(net_fd); if (command == IP) { char *ip = socks_ip_read(net_fd); unsigned short int p = socks_read_port(net_fd); inet_fd = app_connect(IP, (void *)ip, ntohs(p)); if (inet_fd == -1) { app_thread_exit(1, net_fd); } socks5_ip_send_response(net_fd, ip, p); free(ip); break; } else if (command == DOMAIN) { unsigned char size; char *address = socks5_domain_read(net_fd, &size); unsigned short int p = socks_read_port(net_fd); inet_fd = app_connect(DOMAIN, (void *)address, ntohs(p)); if (inet_fd == -1) { app_thread_exit(1, net_fd); } socks5_domain_send_response(net_fd, address, size, p); free(address); break; } else { app_thread_exit(1, net_fd); } } case VERSION4: { if (methods == 1) { char ident[255]; unsigned short int p = socks_read_port(net_fd); char *ip = socks_ip_read(net_fd); socks4_read_nstring(net_fd, ident, sizeof(ident)); if (socks4_is_4a(ip)) { char domain[255]; socks4_read_nstring(net_fd, domain, sizeof(domain)); //log_message("Socks4A: ident:%s; domain:%s;", ident, domain); inet_fd = app_connect(DOMAIN, (void *)domain, ntohs(p)); } else { //log_message("Socks4: connect by ip & port"); inet_fd = app_connect(IP, (void *)ip, ntohs(p)); } if (inet_fd != -1) { socks4_send_response(net_fd, 0x5a); } else { socks4_send_response(net_fd, 0x5b); free(ip); app_thread_exit(1, net_fd); } free(ip); } else { //log_message("Unsupported mode"); } break; } } app_socket_pipe(inet_fd, net_fd); close(inet_fd); app_thread_exit(0, net_fd); return NULL; } int app_loop() { int sock_fd, net_fd; int optval = 1; struct sockaddr_in local, remote; socklen_t remotelen; if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { //log_message("socket()"); exit(1); } if (setsockopt (sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)) < 0) { //log_message("setsockopt()"); exit(1); } memset(&local, 0, sizeof(local)); local.sin_family = AF_INET; local.sin_addr.s_addr = htonl(INADDR_ANY); local.sin_port = htons(port); if (bind(sock_fd, (struct sockaddr *)&local, sizeof(local)) < 0) { //log_message("bind()"); exit(1); } if (listen(sock_fd, 25) < 0) { //log_message("listen()"); exit(1); } remotelen = sizeof(remote); memset(&remote, 0, sizeof(remote)); //log_message("Listening port %d...", port); pthread_t worker; while (1) { if ((net_fd = accept(sock_fd, (struct sockaddr *)&remote, &remotelen)) < 0) { //log_message("accept()"); exit(1); } int one = 1; setsockopt(sock_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)); if (pthread_create (&worker, NULL, &app_thread_process, (void *)&net_fd) == 0) { pthread_detach(worker); } else { //log_message("pthread_create()"); } } } void daemonize() { pid_t pid; int x; pid = fork(); if (pid < 0) { exit(EXIT_FAILURE); } if (pid > 0) { exit(EXIT_SUCCESS); } if (setsid() < 0) { exit(EXIT_FAILURE); } signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); pid = fork(); if (pid < 0) { exit(EXIT_FAILURE); } if (pid > 0) { exit(EXIT_SUCCESS); } umask(0); chdir("/"); for (x = sysconf(_SC_OPEN_MAX); x >= 0; x--) { close(x); } } void usage(char *app) { /* printf ("USAGE: %s [-h][-n PORT][-a AUTHTYPE][-u USERNAME][-p PASSWORD][-l LOGFILE]\n", app); printf("AUTHTYPE: 0 for NOAUTH, 2 for USERPASS\n"); printf ("By default: port is 1080, authtype is no auth, logfile is stdout\n"); */ exit(1); } int main(int argc, char *argv[]) { int ret; log_file = stdout; auth_type = NOAUTH; arg_username = "user"; arg_password = "pass"; pthread_mutex_init(&lock, NULL); signal(SIGPIPE, SIG_IGN); while ((ret = getopt(argc, argv, "n:u:p:l:a:hd")) != -1) { switch (ret) { case 'd':{ daemon_mode = 1; daemonize(); break; } case 'n':{ port = atoi(optarg) & 0xffff; break; } case 'u':{ arg_username = strdup(optarg); break; } case 'p':{ arg_password = strdup(optarg); break; } case 'l':{ freopen(optarg, "wa", log_file); break; } case 'a':{ auth_type = atoi(optarg); break; } case 'h': default: usage(argv[0]); } } // log_message("Starting with authtype %X", auth_type); if (auth_type != NOAUTH) { // log_message("Username is %s, password is %s", arg_username,arg_password); } app_loop(); return 0; }
Editor is loading...