ucspi-socksserver-connected.c (4321B)
1 #include <assert.h> 2 #include <errno.h> 3 #include <stdlib.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <sys/socket.h> 7 #include <netinet/in.h> 8 9 #include <skalibs/exec.h> 10 #include <skalibs/strerr.h> 11 #include <skalibs/types.h> 12 13 #include "socks.h" 14 15 #define PROG "ucspi-socketserver-connect" 16 17 #define SOCKS_RESERVED 0x00 18 #define SOCKS_VERSION_4 0x04 19 #define SOCKS_VERSION_5 0x05 20 21 typedef unsigned char socks5_addr_type_t; 22 #define SOCKS5_ADDR_TYPE_IP4 0x01 23 #define SOCKS5_ADDR_TYPE_DOMAIN 0x03 24 #define SOCKS5_ADDR_TYPE_IP6 0x04 25 26 typedef unsigned char socks5_reply_t; 27 #define SOCKS5_REPLY_OK 0x00 // succeeded 28 #define SOCKS5_REPLY_ERR_GENERAL 0x01 // general SOCKS server failure 29 #define SOCKS5_REPLY_ERR_FORBIDDEN 0x02 // connection not allowed by ruleset 30 #define SOCKS5_REPLY_ERR_NET_UNREACH 0x03 // Network unreachable 31 #define SOCKS5_REPLY_ERR_HOST_UNREACH 0x04 // Host unreachable 32 #define SOCKS5_REPLY_ERR_CONN_REFUSED 0x05 // Connection refused 33 #define SOCKS5_REPLY_ERR_TTL 0x06 // TTL expired 34 #define SOCKS5_REPLY_ERR_CMD 0x07 // Command not supported 35 #define SOCKS5_REPLY_ERR_ATYPE 0x08 // Address type not supported 36 37 void fd_block(int fd) 38 { 39 int flags = fcntl(fd, F_GETFL); 40 if(flags == -1) { 41 strerr_dief1sys(111, "fcntl() getfd"); 42 } 43 if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { 44 strerr_dief1sys(111, "fcntl() setfd"); 45 } 46 } 47 48 void write1(const void *buf, ssize_t n) 49 { 50 ssize_t nwrite, left = n; 51 while (left > 0) { 52 if ((nwrite = write(STDOUT_FILENO, buf, left)) < 0) { 53 if (errno == EINTR || errno == EAGAIN) { 54 continue; 55 } 56 strerr_diefu1sys(111, "write(stdout)"); 57 } else { 58 assert(left >= nwrite); 59 left -= nwrite; 60 buf += nwrite; 61 } 62 } 63 } 64 65 void socks5_reply_ok(socks5_addr_type_t addr_type, void *addr, in_port_t port) 66 { 67 size_t size; 68 switch(addr_type) { 69 case SOCKS5_ADDR_TYPE_IP4: 70 size = 4; 71 break; 72 case SOCKS5_ADDR_TYPE_IP6: 73 size = 16; 74 break; 75 case SOCKS5_ADDR_TYPE_DOMAIN: 76 size = strlen(addr); 77 if(size > 0xff) { 78 strerr_dieinvalid(110, "local hostname too long") ; 79 } 80 break; 81 } 82 83 char reply[] = { 84 SOCKS_VERSION_5, 85 SOCKS5_REPLY_OK, 86 SOCKS_RESERVED, 87 addr_type, 88 }; 89 write1(reply, sizeof(reply)); 90 91 if (addr_type == SOCKS5_ADDR_TYPE_DOMAIN) { 92 /* prefix domain by length */ 93 unsigned char domain_len = size; 94 write1(&domain_len, 1); 95 } 96 write1(addr, size); 97 98 /* should be already in network byte order (from sin_port) */ 99 write1(&port, sizeof(port)); 100 } 101 102 void socks4_send_reply(int status) 103 { 104 char resp[8] = {0x00, (char)status, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 105 write1(resp, sizeof(resp)); 106 } 107 108 int main (int argc, char const *const *argv) 109 { 110 char const *env_socks_version = getenv("SOCKS_VERSION"); 111 if (!env_socks_version) { 112 strerr_dienotset(100, "SOCKS_VERSION"); 113 } 114 115 /* fd_block(STDIN_FILENO); */ 116 fd_block(STDOUT_FILENO); 117 118 if (strcmp(env_socks_version, "5") == 0) { 119 120 struct sockaddr_storage local_addr; 121 socklen_t addrlen = sizeof(local_addr); 122 if(getsockname(6, (struct sockaddr *)&local_addr, &addrlen) != 0) { 123 strerr_diefu1sys(111, "getsockname(6, ...)"); 124 } 125 assert(addrlen <= sizeof(local_addr)); 126 switch(local_addr.ss_family) { 127 case AF_INET: 128 struct sockaddr_in *in_addr = (struct sockaddr_in *)&local_addr; 129 socks5_reply_ok(SOCKS5_ADDR_TYPE_IP4, &in_addr->sin_addr, in_addr->sin_port); 130 break; 131 case AF_INET6: 132 struct sockaddr_in6 *in6_addr = (struct sockaddr_in6 *)&local_addr; 133 socks5_reply_ok(SOCKS5_ADDR_TYPE_IP6, &in6_addr->sin6_addr, in6_addr->sin6_port); 134 break; 135 default: 136 socks5_reply_ok(SOCKS5_ADDR_TYPE_DOMAIN, "localhost", 0); 137 break; 138 } 139 140 } else if (strcmp(env_socks_version, "4") == 0) { 141 socks4_send_reply(SOCKS4_STATUS_OK); 142 143 } else { 144 strerr_dieinvalid(100, "SOCKS_VERSION") ; 145 146 } 147 xexec(argv+1); 148 return 110; 149 } 150 151 /* vim: sw=4 sts=4 et 152 */