├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── lib.c ├── lib.h ├── log.c ├── log.h ├── main.c ├── main.h ├── proto.c ├── proto.h ├── rfc1928.txt ├── rfc1929.txt ├── util.c └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | /tmp 2 | *.o 3 | passwd 4 | server 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | install: true 3 | script: true 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2021 totravel 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 all 13 | 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 THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | CC = gcc 3 | CFLAGS = -c -W -std=gnu99 4 | CFLAGS += $(CFLAG) 5 | LDFLAGS = -lm 6 | 7 | all: server 8 | 9 | server: log.o util.o lib.o proto.o main.o 10 | $(CC) $(LDFLAGS) $^ -o $@ 11 | 12 | log.o: log.c log.h 13 | $(CC) $(CFLAGS) $< -o $@ 14 | 15 | util.o: util.c log.h util.h 16 | $(CC) $(CFLAGS) $< -o $@ 17 | 18 | lib.o: lib.c log.h lib.h 19 | $(CC) $(CFLAGS) $< -o $@ 20 | 21 | proto.o: proto.c log.h util.h proto.h 22 | $(CC) $(CFLAGS) $< -o $@ 23 | 24 | main.o: main.c log.h util.h lib.h main.h 25 | $(CC) $(CFLAGS) $< -o $@ 26 | 27 | clean: 28 | rm -f *.o 29 | rm -f server 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 12 | 13 |

SOCKS V5 server

14 | 15 |

16 | Platform 17 | License 18 | 19 | Lines of code 20 | GitHub code size in bytes 21 | GitHub last commit 22 | Travis (.com) branch 23 |

24 | 25 |

一个简易的 SOCKS V5 代理服务器。

26 | 27 | ## Features 28 | 29 | - 支持 TCP 代理和 UDP 代理 30 | - 支持代理 DNS 查询 31 | - 支持用户名密码认证方式 32 | 33 | ## Build 34 | 35 | 使用 `make` 完成编译和链接。 36 | 37 | ```bash 38 | $ make 39 | ``` 40 | 41 | 若跟上 `CFLAG=-DDEBUG` 则开启调试模式: 42 | 43 | ```bash 44 | $ make CFLAG=-DDEBUG 45 | ``` 46 | 47 | ## Usage 48 | 49 | 不带任何选项启动,则监听 1080 端口,无需认证。 50 | 51 | ```bash 52 | $ ./server 53 | NO AUTHENTICATION REQUIRED 54 | Listening at 0.0.0.0:1080 55 | ``` 56 | 57 | 带上 `-h` 选项则显示帮助信息。 58 | 59 | ```bash 60 | $ ./server -h 61 | usage: ./server [options] 62 | options: 63 | -a
Local Address to bind (default: 0.0.0.0). 64 | -p Port number to bind (default: 1080). 65 | -u The path to passwd. 66 | -d Run as a daemon. 67 | -h Show this help message. 68 | ``` 69 | 70 | 选项 `-a` 和 `-p` 分别用来指定服务器绑定的 IP 地址和端口号。 71 | 72 | ```bash 73 | $ ./server -a 127.0.0.1 -p 8080 74 | NO AUTHENTICATION REQUIRED 75 | Listening at 127.0.0.1:8080 76 | ``` 77 | 78 | 选项 `-u` 用于开启用户名密码认证方式,选项后面必须跟上一个文件的路径。该文件的每一行对应一个用户,用户名和密码之间用逗号 `,` 隔开,例如: 79 | 80 | ```bash 81 | $ cat ./passwd 82 | user1,123456 83 | user2,666 84 | user3,2333 85 | $ ./server -u ./passwd 86 | USERNAME/PASSWORD 87 | 3 users 88 | Listening at 0.0.0.0:1080 89 | ``` 90 | 91 | 若带上 `-d` 参数,服务器将脱离终端,成为守护进程。 92 | 93 | ```bash 94 | $ ./server -d 95 | NO AUTHENTICATION REQUIRED 96 | Listening at 0.0.0.0:1080 97 | PID is [xxxxx] 98 | $ netstat -ntlp | grep xxxxx 99 | tcp 0 0 0.0.0.0:1080 0.0.0.0:* LISTEN xxxxx/./server 100 | ``` 101 | 102 | ## License 103 | 104 | 本项目采用 [MIT](https://opensource.org/licenses/MIT) 开源许可协议。 105 | 106 | ``` 107 | MIT License 108 | 109 | Copyright (c) 2019-2021 totravel 110 | 111 | Permission is hereby granted, free of charge, to any person obtaining a copy 112 | of this software and associated documentation files (the "Software"), to deal 113 | in the Software without restriction, including without limitation the rights 114 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 115 | copies of the Software, and to permit persons to whom the Software is 116 | furnished to do so, subject to the following conditions: 117 | 118 | The above copyright notice and this permission notice shall be included in all 119 | copies or substantial portions of the Software. 120 | 121 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 122 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 123 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 124 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 125 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 126 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 127 | SOFTWARE. 128 | ``` 129 | -------------------------------------------------------------------------------- /lib.c: -------------------------------------------------------------------------------- 1 | 2 | #include "log.h" 3 | #include "lib.h" 4 | 5 | int epfd, nfds; 6 | 7 | void event_init() 8 | { 9 | extern int epfd; 10 | epfd = epoll_create(1); 11 | if (epfd == -1) { 12 | error("epoll_create"); 13 | exit(EXIT_FAILURE); 14 | } 15 | } 16 | 17 | void event_start() 18 | { 19 | extern int epfd, nfds; 20 | struct epoll_event events[MAX_EVENTS]; 21 | struct event_data * fd_data; 22 | for (;;) { 23 | nfds = epoll_wait(epfd, events, MAX_EVENTS, 5000); 24 | if (nfds > 0) { 25 | for (int i = 0; i < nfds; i++) { 26 | fd_data = events[i].data.ptr; 27 | fd_data->cb(fd_data); 28 | } 29 | } else if (nfds == 0) { 30 | } else { 31 | error("epoll_wait"); 32 | exit(EXIT_FAILURE); 33 | } 34 | } 35 | } 36 | 37 | struct event_data * event_set(int fd, cb_t cb) 38 | { 39 | extern int epfd; 40 | struct event_data * fd_data = malloc(sizeof(struct event_data)); 41 | if (!fd_data) { 42 | error("malloc"); 43 | exit(EXIT_FAILURE); 44 | } 45 | fd_data->fd = fd; 46 | fd_data->cb = cb; 47 | fd_data->to = NULL; 48 | struct epoll_event ev; 49 | ev.events = EPOLLIN; 50 | ev.data.ptr = fd_data; 51 | if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) { 52 | error("epoll_ctl"); 53 | exit(EXIT_FAILURE); 54 | } 55 | return fd_data; 56 | } 57 | 58 | void event_clear(struct event_data * fd_data) 59 | { 60 | extern int epfd; 61 | if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd_data->fd, NULL) == -1) { 62 | error("epoll_ctl"); 63 | exit(EXIT_FAILURE); 64 | } 65 | } 66 | 67 | void close_and_free(struct event_data * fd_data) 68 | { 69 | close(fd_data->fd); 70 | if (fd_data->addr) free(fd_data->addr); 71 | free(fd_data); 72 | } 73 | 74 | void event_restart() 75 | { 76 | extern int nfds; 77 | nfds = 0; 78 | } 79 | -------------------------------------------------------------------------------- /lib.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __LIB_H__ 3 | #define __LIB_H__ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define MAX_EVENTS 64 11 | 12 | struct event_data { 13 | int fd; 14 | void (* cb)(struct event_data * fd_data); 15 | struct event_data * to; 16 | struct sockaddr_in * addr; 17 | }; 18 | 19 | typedef void (* cb_t)(struct event_data * fd_data); 20 | 21 | void event_init(); 22 | void event_start(); 23 | struct event_data * event_set(int fd, cb_t cb); 24 | void event_clear(struct event_data * fd_data); 25 | void close_and_free(struct event_data * fd_data); 26 | void event_restart(); 27 | 28 | #endif /* __LIB_H__ */ 29 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | 2 | #include "log.h" 3 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __LOG_H__ 3 | #define __LOG_H__ 4 | 5 | #include 6 | #include // exit() 7 | #include // uint8_t 8 | #include // bool, true, false 9 | #include // strerror() 10 | #include // time(), strftime(), localtime() 11 | #include // errno 12 | 13 | #define TIME_FORMAT "%Y-%m-%d %H:%M:%S" // e.g. 2021-01-04 01:10:38 14 | 15 | #define logger(fmt, ...) \ 16 | do { \ 17 | time_t now = time(NULL); \ 18 | char timestr[20]; \ 19 | strftime(timestr, 20, TIME_FORMAT, localtime(&now));\ 20 | printf("%s " fmt "\n", timestr, ##__VA_ARGS__); \ 21 | } while (0) 22 | 23 | #define error(fmt, ...) logger("ERROR: %s:%d %s(): " fmt ": %s", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__, strerror(errno)) 24 | #define warn(fmt, ...) logger("WARN: %s:%d %s(): " fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__) 25 | #define info(fmt, ...) logger("INFO: %s:%d %s(): " fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__) 26 | #define info_mem(p, l) memdump(p, l); 27 | 28 | #ifdef DEBUG 29 | #define debug(fmt, ...) logger("DEBUG: %s:%d %s(): " fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__) 30 | #define debug_mem(p, l) memdump(p, l); 31 | #else 32 | #define debug(fmt, ...) 33 | #define debug_mem(p, l) 34 | #endif 35 | 36 | #define breakpoint() \ 37 | do { \ 38 | puts("Press any key to continue...");\ 39 | getchar(); \ 40 | } while (0) 41 | 42 | #endif /* __LOG_H__ */ 43 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | 2 | #include "log.h" 3 | #include "util.h" 4 | #include "lib.h" 5 | #include "proto.h" 6 | #include "main.h" 7 | 8 | bool verbose = false; 9 | 10 | uint8_t method = NO_AUTHENTICATION_REQUIRED; 11 | int nusers; 12 | uint8_t ulens[MAX_USERS]; 13 | uint8_t plens[MAX_USERS]; 14 | char * unames[MAX_USERS]; 15 | char * passwds[MAX_USERS]; 16 | 17 | cb_t after_handshake = request_cb; 18 | 19 | void stop_relay_cb(struct event_data * fd_data) 20 | { 21 | debug("from %s", addr2str(fd_data->addr)); 22 | debug(" to %s", addr2str(fd_data->to->addr)); 23 | event_clear(fd_data->to); 24 | close_and_free(fd_data->to); 25 | event_clear(fd_data); 26 | close_and_free(fd_data); 27 | event_restart(); 28 | } 29 | 30 | void stop_udp_relay_cb(struct event_data * fd_data) 31 | { 32 | debug("UDP relay stopped"); 33 | stop_relay_cb(fd_data); 34 | } 35 | 36 | void udp_relay_cb(struct event_data * fd_data) 37 | { 38 | debug("client: %s", addr2str(fd_data->addr)); 39 | uint8_t rx[BUF_SIZE] = {0}; 40 | struct sockaddr_in peer_addr; 41 | socklen_t addrlen = sizeof(struct sockaddr_in); 42 | int buflen = recvfrom(fd_data->fd, rx, BUF_SIZE, 0, (struct sockaddr *)&peer_addr, &addrlen); 43 | if (buflen <= 0) { 44 | error("recvfrom"); 45 | info("client: %s", addr2str(fd_data->addr)); 46 | stop_udp_relay_cb(fd_data); 47 | return; 48 | } 49 | debug("rx: "); 50 | debug_mem(rx, buflen); 51 | debug("peer: %s", addr2str(&peer_addr)); 52 | 53 | if (peer_addr.sin_addr.s_addr == fd_data->addr->sin_addr.s_addr 54 | && peer_addr.sin_port == fd_data->addr->sin_port) { 55 | 56 | struct datagram * dgram = (struct datagram *)rx; 57 | if (dgram->frag != 0 || dgram->atyp == IPV6) { 58 | warn("Dropped"); 59 | info("client: %s", addr2str(fd_data->addr)); 60 | info("rx: "); 61 | info_mem(rx, buflen); 62 | info("peer: %s", addr2str(&peer_addr)); 63 | return; 64 | } 65 | 66 | struct sockaddr_in dst_addr; 67 | memset(&dst_addr, 0, sizeof(struct sockaddr_in)); 68 | dst_addr.sin_family = AF_INET; 69 | dst_addr.sin_port = get_dst_port(&dgram->dst, dgram->atyp); 70 | dst_addr.sin_addr.s_addr = get_dst_addr(&dgram->dst, dgram->atyp); 71 | debug("client -> %s", addr2str(&dst_addr)); 72 | 73 | int payload_len; 74 | uint8_t * payload = get_payload(dgram, buflen, &payload_len); 75 | debug("payload: "); 76 | debug_mem(payload, payload_len); 77 | 78 | if (sendto(fd_data->fd, payload, payload_len, 0, (struct sockaddr *)&dst_addr, sizeof(struct sockaddr_in)) == -1) { 79 | error("sendto"); 80 | info("client: %s", addr2str(fd_data->addr)); 81 | info("rx: "); 82 | info_mem(rx, buflen); 83 | info("peer: %s", addr2str(&peer_addr)); 84 | info("client -> %s", addr2str(&dst_addr)); 85 | info("payload: "); 86 | info_mem(payload, payload_len); 87 | } else { 88 | debug("Payload sent successfully"); 89 | } 90 | } else { 91 | debug("client <- %s", addr2str(&peer_addr)); 92 | uint8_t tx[BUF_SIZE + DGRAM_IPV4_SIZE] = {0}; 93 | struct datagram * dgram = (struct datagram *)tx; 94 | dgram->atyp = IPV4; 95 | dgram->dst.ipv4.addr = peer_addr.sin_addr.s_addr; 96 | dgram->dst.ipv4.port = peer_addr.sin_port; 97 | memcpy((uint8_t *)&dgram->dst.ipv4.port + 2, rx, buflen); 98 | debug("tx: "); 99 | debug_mem(tx, buflen + DGRAM_IPV4_SIZE); 100 | 101 | if (sendto(fd_data->fd, tx, buflen + DGRAM_IPV4_SIZE, 0, (struct sockaddr *)fd_data->addr, sizeof(struct sockaddr_in)) == -1) { 102 | error("sendto"); 103 | info("client: %s", addr2str(fd_data->addr)); 104 | info("rx: "); 105 | info_mem(rx, buflen); 106 | info("tx: "); 107 | info_mem(tx, buflen + DGRAM_IPV4_SIZE); 108 | info("peer: %s", addr2str(&peer_addr)); 109 | } else { 110 | debug("Packet sent successfully"); 111 | } 112 | } 113 | } 114 | 115 | void tcp_relay_cb(struct event_data * fd_data) 116 | { 117 | uint8_t buf[BUF_SIZE]; 118 | int buflen = recv(fd_data->fd, buf, BUF_SIZE, 0); 119 | if (buflen > 0) { 120 | debug("%d bytes <- %s", buflen, addr2str(fd_data->addr)); 121 | debug_mem(buf, buflen); 122 | buflen = send(fd_data->to->fd, buf, buflen, 0); 123 | if (buflen > 0) { 124 | debug("%d bytes -> %s", buflen, addr2str(fd_data->to->addr)); 125 | return; 126 | } else if (errno != ECONNRESET) { 127 | error("send"); 128 | info("from %s", addr2str(fd_data->addr)); 129 | info(" to %s", addr2str(fd_data->to->addr)); 130 | } 131 | } else if (buflen == -1 && errno != ECONNRESET) { 132 | error("recv"); 133 | info("from %s", addr2str(fd_data->addr)); 134 | info(" to %s", addr2str(fd_data->to->addr)); 135 | } 136 | debug("TCP relay stopped"); 137 | stop_relay_cb(fd_data); 138 | } 139 | 140 | void request_cb(struct event_data * fd_data) 141 | { 142 | debug("client: %s", addr2str(fd_data->addr)); 143 | bool reject; 144 | uint8_t rx[MAX_SOCKS_REQUEST_LEN] = {0}, tx[MAX_SOCKS_REPLY_LEN] = {0}; 145 | struct socks_request * req = (struct socks_request *)rx; 146 | struct socks_reply * rep = (struct socks_reply *)tx; 147 | rep->ver = VERSION; 148 | 149 | reject = true; 150 | int buflen = recv(fd_data->fd, rx, MAX_SOCKS_REQUEST_LEN, 0); 151 | if (buflen > 0) { 152 | debug("rx: "); 153 | debug_mem(rx, buflen); 154 | if (buflen < MIN_SOCKS_REQUEST_LEN) { 155 | debug("Invalid SOCKS request"); 156 | } else { 157 | reject = false; 158 | } 159 | } else if (buflen == -1 && errno != ECONNRESET) { 160 | error("recv"); 161 | info("client: %s", addr2str(fd_data->addr)); 162 | } 163 | 164 | if (reject) { 165 | event_clear(fd_data); 166 | close_and_free(fd_data); 167 | debug("Connection closed"); 168 | return; 169 | } 170 | 171 | reject = true; 172 | rep->rep = SUCCEEDED; 173 | if (req->atyp == IPV6) { 174 | rep->rep = ADDRESS_TYPE_NOT_SUPPORTED; 175 | debug("Address type not supported"); 176 | } else { 177 | if (req->cmd == CONNECT) { 178 | in_addr_t addr = get_dst_addr(&req->dst, req->atyp); 179 | in_port_t port = get_dst_port(&req->dst, req->atyp); 180 | int dst_fd = create_and_connect(addr, port); 181 | if (dst_fd) { 182 | fd_data->cb = tcp_relay_cb; 183 | fd_data->to = event_set(dst_fd, tcp_relay_cb); 184 | fd_data->to->to = fd_data; 185 | 186 | fd_data->to->addr = malloc(sizeof(struct sockaddr_in)); 187 | if (!fd_data->to->addr) { 188 | error("malloc"); 189 | exit(EXIT_FAILURE); 190 | } 191 | fd_data->to->addr->sin_port = port; 192 | fd_data->to->addr->sin_addr.s_addr = addr; 193 | 194 | rep->atyp = IPV4; 195 | get_local_addr(dst_fd, &rep->bnd.ipv4.addr, &rep->bnd.ipv4.port); 196 | debug("rep.bnd: %s:%hu", inet_ntoaddr(&rep->bnd.ipv4.addr), ntohs(rep->bnd.ipv4.port)); 197 | reject = false; 198 | } else { 199 | rep->rep = GENERAL_SOCKS_SERVER_FAILURE; 200 | info("client: %s", addr2str(fd_data->addr)); 201 | info("rx: "); 202 | info_mem(rx, buflen); 203 | info("tx: "); 204 | info_mem(tx, SOCKS_REPLY_SIZE_IPV4); 205 | } 206 | } else if (req->cmd == UDP) { 207 | int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 208 | if (fd == -1) { 209 | rep->rep = GENERAL_SOCKS_SERVER_FAILURE; 210 | error("socket"); 211 | } else { 212 | struct sockaddr_in bnd_addr; 213 | memset(&bnd_addr, 0, sizeof(struct sockaddr_in)); 214 | bnd_addr.sin_family = AF_INET; 215 | if (bind(fd, (struct sockaddr *)&bnd_addr, sizeof(struct sockaddr_in)) == -1) { 216 | rep->rep = GENERAL_SOCKS_SERVER_FAILURE; 217 | error("bind"); 218 | close(fd); 219 | } else { 220 | fd_data->to = event_set(fd, udp_relay_cb); 221 | fd_data->to->to = fd_data; 222 | 223 | struct sockaddr_in * home; 224 | home = malloc(sizeof(struct sockaddr_in)); 225 | fd_data->to->addr = home; 226 | if (!home) { 227 | error("malloc"); 228 | exit(EXIT_FAILURE); 229 | } 230 | memset(home, 0, sizeof(struct sockaddr_in)); 231 | home->sin_family = AF_INET; 232 | home->sin_port = get_dst_port(&req->dst, req->atyp); 233 | home->sin_addr.s_addr = get_dst_addr(&req->dst, req->atyp); 234 | if (!home->sin_addr.s_addr) 235 | home->sin_addr.s_addr = fd_data->addr->sin_addr.s_addr; 236 | debug("client: %s(UDP)", addr2str(home)); 237 | if (!home->sin_port) { 238 | rep->rep = ADDRESS_TYPE_NOT_SUPPORTED; 239 | warn("Invalid port"); 240 | info("client: %s", addr2str(fd_data->addr)); 241 | info("rx: "); 242 | info_mem(rx, buflen); 243 | info("tx: "); 244 | info_mem(tx, SOCKS_REPLY_SIZE_IPV4); 245 | info("client: %s(udp)", addr2str(home)); 246 | event_clear(fd_data->to); 247 | close_and_free(fd_data->to); 248 | } else { 249 | rep->atyp = IPV4; 250 | get_local_addr(fd_data->fd, &rep->bnd.ipv4.addr, NULL); 251 | get_local_addr(fd, NULL, &rep->bnd.ipv4.port); 252 | fd_data->cb = stop_udp_relay_cb; 253 | debug("rep.bnd: %s:%hu", inet_ntoaddr(&rep->bnd.ipv4.addr), ntohs(rep->bnd.ipv4.port)); 254 | reject = false; 255 | } 256 | } 257 | } 258 | } else { 259 | rep->rep = COMMAND_NOT_SUPPORTED; 260 | debug("Command not supported"); 261 | } 262 | } 263 | debug("tx: "); 264 | debug_mem(tx, SOCKS_REPLY_SIZE_IPV4); 265 | 266 | if (send(fd_data->fd, tx, SOCKS_REPLY_SIZE_IPV4, 0) > 0) { 267 | debug("SOCKS Reply sent successfully"); 268 | } else { 269 | reject = true; 270 | error("send"); 271 | info("client: %s", addr2str(fd_data->addr)); 272 | info("rx: "); 273 | info_mem(rx, buflen); 274 | info("tx: "); 275 | info_mem(tx, SOCKS_REPLY_SIZE_IPV4); 276 | } 277 | 278 | if (reject) { 279 | if (fd_data->to) { 280 | event_clear(fd_data->to); 281 | close_and_free(fd_data->to); 282 | debug("Disconnect from destination"); 283 | } 284 | event_clear(fd_data); 285 | close_and_free(fd_data); 286 | debug("Connection closed"); 287 | } 288 | } 289 | 290 | void auth_cb(struct event_data * fd_data) 291 | { 292 | debug("client: %s", addr2str(fd_data->addr)); 293 | extern int nusers; 294 | bool reject; 295 | uint8_t rx[MAX_AUTH_REQUEST_LEN] = {0}, tx[AUTH_STATUS_SIZE] = {0}; 296 | struct auth_request * req = (struct auth_request *)rx; 297 | struct auth_status * res = (struct auth_status *)tx; 298 | res->ver = AUTH_VERSION; 299 | 300 | reject = true; 301 | int buflen = recv(fd_data->fd, rx, MAX_AUTH_REQUEST_LEN, 0); 302 | if (buflen > 0) { 303 | debug("rx: "); 304 | debug_mem(rx, buflen); 305 | if (buflen < MIN_AUTH_REQUEST_LEN) { 306 | debug("Invalid auth request"); 307 | } else { 308 | reject = false; 309 | } 310 | } else if (buflen == -1 && errno != ECONNRESET) { 311 | error("recv"); 312 | info("client: %s", addr2str(fd_data->addr)); 313 | } 314 | 315 | if (reject) { 316 | event_clear(fd_data); 317 | close_and_free(fd_data); 318 | debug("Connection closed"); 319 | return; 320 | } 321 | 322 | reject = true; 323 | res->status = AUTH_FAILURE; 324 | uint8_t ulen, plen; 325 | uint8_t * uname, * passwd; 326 | get_uname_passwd(req, &ulen, &uname, &plen, &passwd); 327 | if (verbose) { 328 | char s1[ulen + 1]; strncpy(s1, uname, ulen); 329 | char s2[plen + 1]; strncpy(s2, passwd, plen); 330 | s1[ulen] = '\0'; 331 | s2[plen] = '\0'; 332 | info("username: %d: %s", ulen, s1); 333 | info("password: %d: %s", plen, s2); 334 | } 335 | for (int i = 0; i < nusers; i++) { 336 | if (ulen == ulens[i] 337 | && plen == plens[i] 338 | && !strncmp(unames[i], uname, ulen) 339 | && !strncmp(passwds[i], passwd, plen) 340 | ) { 341 | res->status = AUTH_SUCCESS; 342 | fd_data->cb = request_cb; 343 | reject = false; 344 | break; 345 | } 346 | } 347 | debug("%s", reject ? "Incorrect username or password" : "Authentication successfully"); 348 | debug("tx: "); 349 | debug_mem(tx, AUTH_STATUS_SIZE); 350 | 351 | if (send(fd_data->fd, tx, AUTH_STATUS_SIZE, 0) > 0) { 352 | debug("Status sent successfully."); 353 | } else { 354 | reject = true; 355 | error("send"); 356 | info("client: %s", addr2str(fd_data->addr)); 357 | info("rx: "); 358 | info_mem(rx, buflen); 359 | 360 | char s1[ulen + 1]; strncpy(s1, uname, ulen); 361 | char s2[plen + 1]; strncpy(s2, passwd, plen); 362 | s1[ulen] = '\0'; 363 | s2[plen] = '\0'; 364 | info("uname: %d: %s", ulen, s1); 365 | info("passwd: %d: %s", plen, s2); 366 | 367 | info("tx: "); 368 | info_mem(tx, AUTH_STATUS_SIZE); 369 | } 370 | 371 | if (reject) { 372 | event_clear(fd_data); 373 | close_and_free(fd_data); 374 | debug("Connection closed"); 375 | } 376 | } 377 | 378 | void handshake_cb(struct event_data * fd_data) 379 | { 380 | debug("client: %s", addr2str(fd_data->addr)); 381 | extern uint8_t method; 382 | bool reject; 383 | uint8_t rx[MAX_METHOD_REQUEST_LEN] = {0}, tx[METHOD_REPLY_SIZE] = {0}; 384 | struct method_request * req = (struct method_request *)rx; 385 | struct method_reply * rep = (struct method_reply *)tx; 386 | rep->ver = VERSION; 387 | 388 | reject = true; 389 | int buflen = recv(fd_data->fd, rx, MAX_METHOD_REQUEST_LEN, 0); 390 | if (buflen > 0) { 391 | debug("rx: "); 392 | debug_mem(rx, buflen); 393 | if (buflen < MIN_METHOD_REQUEST_LEN) { 394 | debug("Invalid version identifier/method selection message"); 395 | } else { 396 | reject = false; 397 | } 398 | } else if (buflen == -1 && errno != ECONNRESET) { 399 | error("recv"); 400 | info("client: %s", addr2str(fd_data->addr)); 401 | } 402 | 403 | if (reject) { 404 | event_clear(fd_data); 405 | close_and_free(fd_data); 406 | debug("Connection closed"); 407 | return; 408 | } 409 | 410 | reject = true; 411 | rep->method = NO_ACCEPTABLE_METHODS; 412 | if ((int)req->ver == VERSION) { 413 | if (method_exists(req, method)) { 414 | rep->method = method; 415 | fd_data->cb = after_handshake; 416 | reject = false; 417 | } else { 418 | debug("No acceptable methods"); 419 | } 420 | } else { 421 | debug("Protocol version not supported"); 422 | } 423 | debug("tx: "); 424 | debug_mem(tx, METHOD_REPLY_SIZE); 425 | 426 | if (send(fd_data->fd, tx, METHOD_REPLY_SIZE, 0) > 0) { 427 | debug("METHOD selection message sent successfully"); 428 | } else { 429 | reject = true; 430 | error("send"); 431 | info("client: %s", addr2str(fd_data->addr)); 432 | info("rx: "); 433 | info_mem(rx, buflen); 434 | info("tx: "); 435 | info_mem(tx, METHOD_REPLY_SIZE); 436 | } 437 | 438 | if (reject) { 439 | event_clear(fd_data); 440 | close_and_free(fd_data); 441 | debug("Connection closed"); 442 | } 443 | } 444 | 445 | void accept_cb(struct event_data * fd_data) 446 | { 447 | socklen_t addrlen = sizeof(struct sockaddr_in); 448 | struct sockaddr_in * client_addr = malloc(sizeof(struct sockaddr_in)); 449 | if (!client_addr) { 450 | error("malloc"); 451 | exit(EXIT_FAILURE); 452 | } 453 | int fd = accept(fd_data->fd, (struct sockaddr *)client_addr, &addrlen); 454 | if (fd == -1) { 455 | error("accept"); 456 | } else { 457 | fd_data = event_set(fd, handshake_cb); 458 | fd_data->addr = client_addr; 459 | debug("Connection from %s", addr2str(fd_data->addr)); 460 | } 461 | } 462 | 463 | bool load_users(const char * path) 464 | { 465 | extern int nusers; 466 | extern uint8_t ulens[MAX_USERS]; 467 | extern uint8_t plens[MAX_USERS]; 468 | extern char * unames[MAX_USERS]; 469 | extern char * passwds[MAX_USERS]; 470 | FILE * fp = fopen(path, "r"); 471 | if (fp == 0) { 472 | error("fopen"); 473 | info("path: %s", path); 474 | return false; 475 | } 476 | int i = 0, ulen, plen; 477 | char row[MAX_ULEN + MAX_PLEN + 4]; // e.g. user1,123456\r\n\0 478 | char * p, * end; 479 | do { 480 | memset(row, 0, sizeof(row)); 481 | if (fgets(row, MAX_ULEN + MAX_PLEN + 4, fp)) { 482 | p = strchr(row, ','); 483 | end = strchr(p, '\0'); 484 | if (*(end - 1) == '\n') { 485 | end--; 486 | if (*(end - 1) == '\r') 487 | end--; 488 | } 489 | ulen = ulens[i] = p - row; 490 | plen = plens[i] = end - p - 1; 491 | unames[i] = malloc(ulen); 492 | if (!unames[i]) { 493 | error("malloc"); 494 | exit(EXIT_FAILURE); 495 | } 496 | passwds[i] = malloc(plen); 497 | if (!passwds[i]) { 498 | error("malloc"); 499 | exit(EXIT_FAILURE); 500 | } 501 | strncpy(unames[i], row, ulen); 502 | strncpy(passwds[i], p + 1, plen); 503 | i++; 504 | if (verbose) { 505 | char s1[ulen + 1]; strncpy(s1, row, ulen); 506 | char s2[plen + 1]; strncpy(s2, p + 1, plen); 507 | s1[ulen] = '\0'; 508 | s2[plen] = '\0'; 509 | info("username: %d: %s", ulen, s1); 510 | info("password: %d: %s", plen, s2); 511 | } 512 | } else { 513 | break; 514 | } 515 | } while (!feof(fp)); 516 | fclose(fp); 517 | nusers = i; 518 | debug("%d users", nusers); 519 | return true; 520 | } 521 | 522 | void usage(const char * name) 523 | { 524 | printf( 525 | "usage: %s [options]\n" 526 | "options: \n" 527 | " -a
Local Address to bind (default: 0.0.0.0).\n" 528 | " -p Port number to bind (default: 1080).\n" 529 | " -u The path to passwd.\n" 530 | " -d Run as a daemon.\n" 531 | " -h Show this help message.\n", name); 532 | } 533 | 534 | int main(int argc, char * argv[]) 535 | { 536 | extern uint8_t method; 537 | extern int nusers; 538 | extern bool verbose; 539 | 540 | if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { 541 | error("signal"); 542 | exit(EXIT_FAILURE); 543 | } 544 | 545 | bool daemon = false; 546 | in_addr_t addr = htonl(DEFAULT_ADDR); 547 | in_port_t port = htons(DEFAULT_PORT); 548 | 549 | int opt; 550 | opterr = 0; 551 | for (;;) { 552 | opt = getopt(argc, argv, ":a:p:u:dhv"); 553 | if (opt == -1) break; 554 | switch (opt) { 555 | case 'a': 556 | addr = inet_addr(optarg); 557 | break; 558 | case 'p': 559 | port = htons(atoi(optarg)); 560 | break; 561 | case 'u': 562 | if (!load_users(optarg)) exit(EXIT_FAILURE); 563 | method = USERNAME_PASSWORD; 564 | after_handshake = auth_cb; 565 | break; 566 | case 'd': 567 | daemon = true; 568 | break; 569 | case 'h': 570 | usage(argv[0]); 571 | exit(EXIT_SUCCESS); 572 | case 'v': 573 | verbose = true; 574 | break; 575 | case ':': 576 | printf("Missing argument after: -%c\n", optopt); 577 | usage(argv[0]); 578 | exit(EXIT_FAILURE); 579 | case '?': 580 | printf("Invalid argument: -%c\n", optopt); 581 | usage(argv[0]); 582 | exit(EXIT_FAILURE); 583 | } 584 | } 585 | 586 | if (method == USERNAME_PASSWORD) { 587 | puts("USERNAME/PASSWORD"); 588 | printf("%d users\n", nusers); 589 | } else { 590 | puts("NO AUTHENTICATION REQUIRED"); 591 | } 592 | 593 | int fd = create_and_listen(addr, port); 594 | if (!fd) exit(EXIT_FAILURE); 595 | printf("Listening on %s:%hu\n", inet_ntoaddr(&addr), ntohs(port)); 596 | 597 | if (daemon) { 598 | pid_t pid = fork(); 599 | if (pid == -1) { 600 | perror("fork"); 601 | exit(EXIT_FAILURE); 602 | } 603 | if (pid > 0) { 604 | printf("PID is %d\n", pid); 605 | exit(EXIT_SUCCESS); 606 | } 607 | } 608 | 609 | event_init(); 610 | event_set(fd, accept_cb); 611 | event_start(); 612 | } 613 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | 2 | #include // bool, true, false 3 | #include // uint8_t 4 | #include // memset() 5 | #include // recv(), send() 6 | #include // inet_ntoa() 7 | #include 8 | 9 | #define DEFAULT_ADDR INADDR_ANY 10 | #define DEFAULT_PORT 1080 11 | 12 | #define BUF_SIZE 10240 13 | #define MAX_USERS 10 14 | 15 | void stop_relay_cb(struct event_data * fd_data); 16 | void udp_relay_cb(struct event_data * fd_data); 17 | void tcp_relay_cb(struct event_data * fd_data); 18 | void request_cb(struct event_data * fd_data); 19 | void auth_cb(struct event_data * fd_data); 20 | void handshake_cb(struct event_data * fd_data); 21 | void accept_cb(struct event_data * fd_data); 22 | bool load_users(const char * path); 23 | void usage(const char * name); 24 | -------------------------------------------------------------------------------- /proto.c: -------------------------------------------------------------------------------- 1 | 2 | #include "log.h" 3 | #include "util.h" 4 | #include "proto.h" 5 | 6 | bool method_exists(struct method_request * method_req, uint8_t method) 7 | { 8 | for (int i = 0; i < method_req->nmethods; i++) 9 | if (method_req->methods[i] == method) return true; 10 | return false; 11 | } 12 | 13 | void get_uname_passwd(struct auth_request * req, uint8_t * ulen, uint8_t ** uname, uint8_t * plen, uint8_t ** passwd) 14 | { 15 | *ulen = req->ulen; 16 | *uname = req->uname; 17 | *plen = *(req->uname + req->ulen); 18 | *passwd = req->uname + req->ulen + 1; 19 | } 20 | 21 | in_addr_t get_dst_addr(union dst_or_bnd * dst, uint8_t atyp) 22 | { 23 | if (atyp == IPV4) return dst->ipv4.addr; 24 | char domain[64]; 25 | size_t len = dst->domain.len; 26 | domain[len] = '\0'; 27 | strncpy(domain, dst->domain.str, len); 28 | return resolve_domain(domain); 29 | } 30 | 31 | in_port_t get_dst_port(union dst_or_bnd * dst, uint8_t atyp) 32 | { 33 | if (atyp == IPV4) return dst->ipv4.port; 34 | return *(in_port_t *)(dst->domain.str + dst->domain.len); 35 | } 36 | 37 | uint8_t * get_payload(struct datagram * dgram, int buflen, int * len) 38 | { 39 | uint8_t * payload = dgram->atyp == IPV4 ? (uint8_t *)&dgram->dst.ipv4.port + 2 : dgram->dst.domain.str + dgram->dst.domain.len + 2; 40 | *len = buflen - (payload - (uint8_t *)dgram); 41 | return payload; 42 | } 43 | -------------------------------------------------------------------------------- /proto.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __PROTO_H__ 3 | #define __PROTO_H__ 4 | 5 | #include 6 | #include 7 | #include 8 | #include // close() 9 | #include 10 | 11 | // VERSION 12 | #define VERSION 0x05 13 | #define AUTH_VERSION 0x01 14 | 15 | // Method 16 | #define NO_AUTHENTICATION_REQUIRED 0x00 17 | #define GSSAPI 0x01 18 | #define USERNAME_PASSWORD 0x02 19 | #define NO_ACCEPTABLE_METHODS 0xff 20 | 21 | // Status 22 | #define AUTH_SUCCESS 0x00 23 | #define AUTH_FAILURE 0x01 24 | 25 | // Command 26 | #define CONNECT 0x01 27 | #define BIND 0x02 28 | #define UDP 0x03 29 | 30 | // Address type 31 | #define IPV4 0x01 32 | #define DOMAIN 0x03 33 | #define IPV6 0x04 34 | 35 | // Reply 36 | #define SUCCEEDED 0x00 37 | #define GENERAL_SOCKS_SERVER_FAILURE 0x01 38 | #define CONNECTION_NOT_ALLOWED_BY_RULESET 0x02 39 | #define NETWORK_UNREACHABLE 0x03 40 | #define HOST_UNREACHABLE 0x04 41 | #define CONNECTION_REFUSED 0x05 42 | #define TTL_EXPIRED 0x06 43 | #define COMMAND_NOT_SUPPORTED 0x07 44 | #define ADDRESS_TYPE_NOT_SUPPORTED 0x08 45 | 46 | // Length limit 47 | #define MAX_ULEN 255 48 | #define MAX_PLEN 255 49 | #define MAX_METHOD_REQUEST_LEN 257 50 | #define MIN_METHOD_REQUEST_LEN 3 51 | #define MAX_AUTH_REQUEST_LEN 513 52 | #define MIN_AUTH_REQUEST_LEN 5 53 | #define MAX_SOCKS_REQUEST_LEN 69 54 | #define MIN_SOCKS_REQUEST_LEN 10 55 | #define MAX_SOCKS_REPLY_LEN MAX_SOCKS_REQUEST_LEN 56 | 57 | // Size 58 | #define METHOD_REPLY_SIZE 2 59 | #define AUTH_STATUS_SIZE 2 60 | #define DGRAM_IPV4_SIZE 10 61 | #define SOCKS_REPLY_SIZE_IPV4 MIN_SOCKS_REQUEST_LEN 62 | 63 | // version identifier/method selection message 64 | struct method_request { 65 | uint8_t ver; 66 | uint8_t nmethods; 67 | uint8_t methods[]; 68 | }; 69 | 70 | // method selection message 71 | struct method_reply { 72 | uint8_t ver; 73 | uint8_t method; 74 | }; 75 | 76 | struct auth_request { 77 | uint8_t ver; 78 | uint8_t ulen; 79 | uint8_t uname[]; 80 | }; 81 | 82 | struct auth_status { 83 | uint8_t ver; 84 | uint8_t status; 85 | }; 86 | 87 | union dst_or_bnd { 88 | struct { 89 | in_addr_t addr; 90 | in_port_t port; 91 | } ipv4; 92 | struct { 93 | uint8_t len; 94 | uint8_t str[]; 95 | } domain; 96 | struct { 97 | uint8_t addr[16]; 98 | in_port_t port; 99 | } ipv6; 100 | }; 101 | 102 | struct socks_request { 103 | uint8_t ver; 104 | uint8_t cmd; 105 | uint8_t rsv; 106 | uint8_t atyp; 107 | union dst_or_bnd dst; 108 | }; 109 | 110 | struct socks_reply { 111 | uint8_t ver; 112 | uint8_t rep; 113 | uint8_t rsv; 114 | uint8_t atyp; 115 | union dst_or_bnd bnd; 116 | }; 117 | 118 | struct datagram { 119 | uint16_t rsv; 120 | uint8_t frag; 121 | uint8_t atyp; 122 | union dst_or_bnd dst; 123 | }; 124 | 125 | bool method_exists(struct method_request * method_req, uint8_t method); 126 | void get_uname_passwd(struct auth_request * req, uint8_t * ulen, uint8_t ** uname, uint8_t * plen, uint8_t ** passwd); 127 | in_addr_t get_dst_addr(union dst_or_bnd * dst, uint8_t atyp); 128 | in_port_t get_dst_port(union dst_or_bnd * dst, uint8_t atyp); 129 | uint8_t * get_payload(struct datagram * dgram, int buflen, int * len); 130 | 131 | #endif /* __PROTO_H__ */ 132 | -------------------------------------------------------------------------------- /rfc1928.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Network Working Group M. Leech 8 | Request for Comments: 1928 Bell-Northern Research Ltd 9 | Category: Standards Track M. Ganis 10 | International Business Machines 11 | Y. Lee 12 | NEC Systems Laboratory 13 | R. Kuris 14 | Unify Corporation 15 | D. Koblas 16 | Independent Consultant 17 | L. Jones 18 | Hewlett-Packard Company 19 | March 1996 20 | 21 | 22 | SOCKS Protocol Version 5 23 | 24 | Status of this Memo 25 | 26 | This document specifies an Internet standards track protocol for the 27 | Internet community, and requests discussion and suggestions for 28 | improvements. Please refer to the current edition of the "Internet 29 | Official Protocol Standards" (STD 1) for the standardization state 30 | and status of this protocol. Distribution of this memo is unlimited. 31 | 32 | Acknowledgments 33 | 34 | This memo describes a protocol that is an evolution of the previous 35 | version of the protocol, version 4 [1]. This new protocol stems from 36 | active discussions and prototype implementations. The key 37 | contributors are: Marcus Leech: Bell-Northern Research, David Koblas: 38 | Independent Consultant, Ying-Da Lee: NEC Systems Laboratory, LaMont 39 | Jones: Hewlett-Packard Company, Ron Kuris: Unify Corporation, Matt 40 | Ganis: International Business Machines. 41 | 42 | 1. Introduction 43 | 44 | The use of network firewalls, systems that effectively isolate an 45 | organizations internal network structure from an exterior network, 46 | such as the INTERNET is becoming increasingly popular. These 47 | firewall systems typically act as application-layer gateways between 48 | networks, usually offering controlled TELNET, FTP, and SMTP access. 49 | With the emergence of more sophisticated application layer protocols 50 | designed to facilitate global information discovery, there exists a 51 | need to provide a general framework for these protocols to 52 | transparently and securely traverse a firewall. 53 | 54 | 55 | 56 | 57 | 58 | Leech, et al Standards Track [Page 1] 59 | 60 | RFC 1928 SOCKS Protocol Version 5 March 1996 61 | 62 | 63 | There exists, also, a need for strong authentication of such 64 | traversal in as fine-grained a manner as is practical. This 65 | requirement stems from the realization that client-server 66 | relationships emerge between the networks of various organizations, 67 | and that such relationships need to be controlled and often strongly 68 | authenticated. 69 | 70 | The protocol described here is designed to provide a framework for 71 | client-server applications in both the TCP and UDP domains to 72 | conveniently and securely use the services of a network firewall. 73 | The protocol is conceptually a "shim-layer" between the application 74 | layer and the transport layer, and as such does not provide network- 75 | layer gateway services, such as forwarding of ICMP messages. 76 | 77 | 2. Existing practice 78 | 79 | There currently exists a protocol, SOCKS Version 4, that provides for 80 | unsecured firewall traversal for TCP-based client-server 81 | applications, including TELNET, FTP and the popular information- 82 | discovery protocols such as HTTP, WAIS and GOPHER. 83 | 84 | This new protocol extends the SOCKS Version 4 model to include UDP, 85 | and extends the framework to include provisions for generalized 86 | strong authentication schemes, and extends the addressing scheme to 87 | encompass domain-name and V6 IP addresses. 88 | 89 | The implementation of the SOCKS protocol typically involves the 90 | recompilation or relinking of TCP-based client applications to use 91 | the appropriate encapsulation routines in the SOCKS library. 92 | 93 | Note: 94 | 95 | Unless otherwise noted, the decimal numbers appearing in packet- 96 | format diagrams represent the length of the corresponding field, in 97 | octets. Where a given octet must take on a specific value, the 98 | syntax X'hh' is used to denote the value of the single octet in that 99 | field. When the word 'Variable' is used, it indicates that the 100 | corresponding field has a variable length defined either by an 101 | associated (one or two octet) length field, or by a data type field. 102 | 103 | 3. Procedure for TCP-based clients 104 | 105 | When a TCP-based client wishes to establish a connection to an object 106 | that is reachable only via a firewall (such determination is left up 107 | to the implementation), it must open a TCP connection to the 108 | appropriate SOCKS port on the SOCKS server system. The SOCKS service 109 | is conventionally located on TCP port 1080. If the connection 110 | request succeeds, the client enters a negotiation for the 111 | 112 | 113 | 114 | Leech, et al Standards Track [Page 2] 115 | 116 | RFC 1928 SOCKS Protocol Version 5 March 1996 117 | 118 | 119 | authentication method to be used, authenticates with the chosen 120 | method, then sends a relay request. The SOCKS server evaluates the 121 | request, and either establishes the appropriate connection or denies 122 | it. 123 | 124 | Unless otherwise noted, the decimal numbers appearing in packet- 125 | format diagrams represent the length of the corresponding field, in 126 | octets. Where a given octet must take on a specific value, the 127 | syntax X'hh' is used to denote the value of the single octet in that 128 | field. When the word 'Variable' is used, it indicates that the 129 | corresponding field has a variable length defined either by an 130 | associated (one or two octet) length field, or by a data type field. 131 | 132 | The client connects to the server, and sends a version 133 | identifier/method selection message: 134 | 135 | +----+----------+----------+ 136 | |VER | NMETHODS | METHODS | 137 | +----+----------+----------+ 138 | | 1 | 1 | 1 to 255 | 139 | +----+----------+----------+ 140 | 141 | The VER field is set to X'05' for this version of the protocol. The 142 | NMETHODS field contains the number of method identifier octets that 143 | appear in the METHODS field. 144 | 145 | The server selects from one of the methods given in METHODS, and 146 | sends a METHOD selection message: 147 | 148 | +----+--------+ 149 | |VER | METHOD | 150 | +----+--------+ 151 | | 1 | 1 | 152 | +----+--------+ 153 | 154 | If the selected METHOD is X'FF', none of the methods listed by the 155 | client are acceptable, and the client MUST close the connection. 156 | 157 | The values currently defined for METHOD are: 158 | 159 | o X'00' NO AUTHENTICATION REQUIRED 160 | o X'01' GSSAPI 161 | o X'02' USERNAME/PASSWORD 162 | o X'03' to X'7F' IANA ASSIGNED 163 | o X'80' to X'FE' RESERVED FOR PRIVATE METHODS 164 | o X'FF' NO ACCEPTABLE METHODS 165 | 166 | The client and server then enter a method-specific sub-negotiation. 167 | 168 | 169 | 170 | Leech, et al Standards Track [Page 3] 171 | 172 | RFC 1928 SOCKS Protocol Version 5 March 1996 173 | 174 | 175 | Descriptions of the method-dependent sub-negotiations appear in 176 | separate memos. 177 | 178 | Developers of new METHOD support for this protocol should contact 179 | IANA for a METHOD number. The ASSIGNED NUMBERS document should be 180 | referred to for a current list of METHOD numbers and their 181 | corresponding protocols. 182 | 183 | Compliant implementations MUST support GSSAPI and SHOULD support 184 | USERNAME/PASSWORD authentication methods. 185 | 186 | 4. Requests 187 | 188 | Once the method-dependent subnegotiation has completed, the client 189 | sends the request details. If the negotiated method includes 190 | encapsulation for purposes of integrity checking and/or 191 | confidentiality, these requests MUST be encapsulated in the method- 192 | dependent encapsulation. 193 | 194 | The SOCKS request is formed as follows: 195 | 196 | +----+-----+-------+------+----------+----------+ 197 | |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | 198 | +----+-----+-------+------+----------+----------+ 199 | | 1 | 1 | X'00' | 1 | Variable | 2 | 200 | +----+-----+-------+------+----------+----------+ 201 | 202 | Where: 203 | 204 | o VER protocol version: X'05' 205 | o CMD 206 | o CONNECT X'01' 207 | o BIND X'02' 208 | o UDP ASSOCIATE X'03' 209 | o RSV RESERVED 210 | o ATYP address type of following address 211 | o IP V4 address: X'01' 212 | o DOMAINNAME: X'03' 213 | o IP V6 address: X'04' 214 | o DST.ADDR desired destination address 215 | o DST.PORT desired destination port in network octet 216 | order 217 | 218 | The SOCKS server will typically evaluate the request based on source 219 | and destination addresses, and return one or more reply messages, as 220 | appropriate for the request type. 221 | 222 | 223 | 224 | 225 | 226 | Leech, et al Standards Track [Page 4] 227 | 228 | RFC 1928 SOCKS Protocol Version 5 March 1996 229 | 230 | 231 | 5. Addressing 232 | 233 | In an address field (DST.ADDR, BND.ADDR), the ATYP field specifies 234 | the type of address contained within the field: 235 | 236 | o X'01' 237 | 238 | the address is a version-4 IP address, with a length of 4 octets 239 | 240 | o X'03' 241 | 242 | the address field contains a fully-qualified domain name. The first 243 | octet of the address field contains the number of octets of name that 244 | follow, there is no terminating NUL octet. 245 | 246 | o X'04' 247 | 248 | the address is a version-6 IP address, with a length of 16 octets. 249 | 250 | 6. Replies 251 | 252 | The SOCKS request information is sent by the client as soon as it has 253 | established a connection to the SOCKS server, and completed the 254 | authentication negotiations. The server evaluates the request, and 255 | returns a reply formed as follows: 256 | 257 | +----+-----+-------+------+----------+----------+ 258 | |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | 259 | +----+-----+-------+------+----------+----------+ 260 | | 1 | 1 | X'00' | 1 | Variable | 2 | 261 | +----+-----+-------+------+----------+----------+ 262 | 263 | Where: 264 | 265 | o VER protocol version: X'05' 266 | o REP Reply field: 267 | o X'00' succeeded 268 | o X'01' general SOCKS server failure 269 | o X'02' connection not allowed by ruleset 270 | o X'03' Network unreachable 271 | o X'04' Host unreachable 272 | o X'05' Connection refused 273 | o X'06' TTL expired 274 | o X'07' Command not supported 275 | o X'08' Address type not supported 276 | o X'09' to X'FF' unassigned 277 | o RSV RESERVED 278 | o ATYP address type of following address 279 | 280 | 281 | 282 | Leech, et al Standards Track [Page 5] 283 | 284 | RFC 1928 SOCKS Protocol Version 5 March 1996 285 | 286 | 287 | o IP V4 address: X'01' 288 | o DOMAINNAME: X'03' 289 | o IP V6 address: X'04' 290 | o BND.ADDR server bound address 291 | o BND.PORT server bound port in network octet order 292 | 293 | Fields marked RESERVED (RSV) must be set to X'00'. 294 | 295 | If the chosen method includes encapsulation for purposes of 296 | authentication, integrity and/or confidentiality, the replies are 297 | encapsulated in the method-dependent encapsulation. 298 | 299 | CONNECT 300 | 301 | In the reply to a CONNECT, BND.PORT contains the port number that the 302 | server assigned to connect to the target host, while BND.ADDR 303 | contains the associated IP address. The supplied BND.ADDR is often 304 | different from the IP address that the client uses to reach the SOCKS 305 | server, since such servers are often multi-homed. It is expected 306 | that the SOCKS server will use DST.ADDR and DST.PORT, and the 307 | client-side source address and port in evaluating the CONNECT 308 | request. 309 | 310 | BIND 311 | 312 | The BIND request is used in protocols which require the client to 313 | accept connections from the server. FTP is a well-known example, 314 | which uses the primary client-to-server connection for commands and 315 | status reports, but may use a server-to-client connection for 316 | transferring data on demand (e.g. LS, GET, PUT). 317 | 318 | It is expected that the client side of an application protocol will 319 | use the BIND request only to establish secondary connections after a 320 | primary connection is established using CONNECT. In is expected that 321 | a SOCKS server will use DST.ADDR and DST.PORT in evaluating the BIND 322 | request. 323 | 324 | Two replies are sent from the SOCKS server to the client during a 325 | BIND operation. The first is sent after the server creates and binds 326 | a new socket. The BND.PORT field contains the port number that the 327 | SOCKS server assigned to listen for an incoming connection. The 328 | BND.ADDR field contains the associated IP address. The client will 329 | typically use these pieces of information to notify (via the primary 330 | or control connection) the application server of the rendezvous 331 | address. The second reply occurs only after the anticipated incoming 332 | connection succeeds or fails. 333 | 334 | 335 | 336 | 337 | 338 | Leech, et al Standards Track [Page 6] 339 | 340 | RFC 1928 SOCKS Protocol Version 5 March 1996 341 | 342 | 343 | In the second reply, the BND.PORT and BND.ADDR fields contain the 344 | address and port number of the connecting host. 345 | 346 | UDP ASSOCIATE 347 | 348 | The UDP ASSOCIATE request is used to establish an association within 349 | the UDP relay process to handle UDP datagrams. The DST.ADDR and 350 | DST.PORT fields contain the address and port that the client expects 351 | to use to send UDP datagrams on for the association. The server MAY 352 | use this information to limit access to the association. If the 353 | client is not in possesion of the information at the time of the UDP 354 | ASSOCIATE, the client MUST use a port number and address of all 355 | zeros. 356 | 357 | A UDP association terminates when the TCP connection that the UDP 358 | ASSOCIATE request arrived on terminates. 359 | 360 | In the reply to a UDP ASSOCIATE request, the BND.PORT and BND.ADDR 361 | fields indicate the port number/address where the client MUST send 362 | UDP request messages to be relayed. 363 | 364 | Reply Processing 365 | 366 | When a reply (REP value other than X'00') indicates a failure, the 367 | SOCKS server MUST terminate the TCP connection shortly after sending 368 | the reply. This must be no more than 10 seconds after detecting the 369 | condition that caused a failure. 370 | 371 | If the reply code (REP value of X'00') indicates a success, and the 372 | request was either a BIND or a CONNECT, the client may now start 373 | passing data. If the selected authentication method supports 374 | encapsulation for the purposes of integrity, authentication and/or 375 | confidentiality, the data are encapsulated using the method-dependent 376 | encapsulation. Similarly, when data arrives at the SOCKS server for 377 | the client, the server MUST encapsulate the data as appropriate for 378 | the authentication method in use. 379 | 380 | 7. Procedure for UDP-based clients 381 | 382 | A UDP-based client MUST send its datagrams to the UDP relay server at 383 | the UDP port indicated by BND.PORT in the reply to the UDP ASSOCIATE 384 | request. If the selected authentication method provides 385 | encapsulation for the purposes of authenticity, integrity, and/or 386 | confidentiality, the datagram MUST be encapsulated using the 387 | appropriate encapsulation. Each UDP datagram carries a UDP request 388 | header with it: 389 | 390 | 391 | 392 | 393 | 394 | Leech, et al Standards Track [Page 7] 395 | 396 | RFC 1928 SOCKS Protocol Version 5 March 1996 397 | 398 | 399 | +----+------+------+----------+----------+----------+ 400 | |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | 401 | +----+------+------+----------+----------+----------+ 402 | | 2 | 1 | 1 | Variable | 2 | Variable | 403 | +----+------+------+----------+----------+----------+ 404 | 405 | The fields in the UDP request header are: 406 | 407 | o RSV Reserved X'0000' 408 | o FRAG Current fragment number 409 | o ATYP address type of following addresses: 410 | o IP V4 address: X'01' 411 | o DOMAINNAME: X'03' 412 | o IP V6 address: X'04' 413 | o DST.ADDR desired destination address 414 | o DST.PORT desired destination port 415 | o DATA user data 416 | 417 | When a UDP relay server decides to relay a UDP datagram, it does so 418 | silently, without any notification to the requesting client. 419 | Similarly, it will drop datagrams it cannot or will not relay. When 420 | a UDP relay server receives a reply datagram from a remote host, it 421 | MUST encapsulate that datagram using the above UDP request header, 422 | and any authentication-method-dependent encapsulation. 423 | 424 | The UDP relay server MUST acquire from the SOCKS server the expected 425 | IP address of the client that will send datagrams to the BND.PORT 426 | given in the reply to UDP ASSOCIATE. It MUST drop any datagrams 427 | arriving from any source IP address other than the one recorded for 428 | the particular association. 429 | 430 | The FRAG field indicates whether or not this datagram is one of a 431 | number of fragments. If implemented, the high-order bit indicates 432 | end-of-fragment sequence, while a value of X'00' indicates that this 433 | datagram is standalone. Values between 1 and 127 indicate the 434 | fragment position within a fragment sequence. Each receiver will 435 | have a REASSEMBLY QUEUE and a REASSEMBLY TIMER associated with these 436 | fragments. The reassembly queue must be reinitialized and the 437 | associated fragments abandoned whenever the REASSEMBLY TIMER expires, 438 | or a new datagram arrives carrying a FRAG field whose value is less 439 | than the highest FRAG value processed for this fragment sequence. 440 | The reassembly timer MUST be no less than 5 seconds. It is 441 | recommended that fragmentation be avoided by applications wherever 442 | possible. 443 | 444 | Implementation of fragmentation is optional; an implementation that 445 | does not support fragmentation MUST drop any datagram whose FRAG 446 | field is other than X'00'. 447 | 448 | 449 | 450 | Leech, et al Standards Track [Page 8] 451 | 452 | RFC 1928 SOCKS Protocol Version 5 March 1996 453 | 454 | 455 | The programming interface for a SOCKS-aware UDP MUST report an 456 | available buffer space for UDP datagrams that is smaller than the 457 | actual space provided by the operating system: 458 | 459 | o if ATYP is X'01' - 10+method_dependent octets smaller 460 | o if ATYP is X'03' - 262+method_dependent octets smaller 461 | o if ATYP is X'04' - 20+method_dependent octets smaller 462 | 463 | 8. Security Considerations 464 | 465 | This document describes a protocol for the application-layer 466 | traversal of IP network firewalls. The security of such traversal is 467 | highly dependent on the particular authentication and encapsulation 468 | methods provided in a particular implementation, and selected during 469 | negotiation between SOCKS client and SOCKS server. 470 | 471 | Careful consideration should be given by the administrator to the 472 | selection of authentication methods. 473 | 474 | 9. References 475 | 476 | [1] Koblas, D., "SOCKS", Proceedings: 1992 Usenix Security Symposium. 477 | 478 | Author's Address 479 | 480 | Marcus Leech 481 | Bell-Northern Research Ltd 482 | P.O. Box 3511, Stn. C, 483 | Ottawa, ON 484 | CANADA K1Y 4H7 485 | 486 | Phone: (613) 763-9145 487 | EMail: mleech@bnr.ca 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | Leech, et al Standards Track [Page 9] 507 | 508 | -------------------------------------------------------------------------------- /rfc1929.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Network Working Group M. Leech 8 | Request for Comments: 1929 Bell-Northern Research Ltd 9 | Category: Standards Track March 1996 10 | 11 | 12 | Username/Password Authentication for SOCKS V5 13 | 14 | Status of this Memo 15 | 16 | This document specifies an Internet standards track protocol for the 17 | Internet community, and requests discussion and suggestions for 18 | improvements. Please refer to the current edition of the "Internet 19 | Official Protocol Standards" (STD 1) for the standardization state 20 | and status of this protocol. Distribution of this memo is unlimited. 21 | 22 | 1. Introduction 23 | 24 | The protocol specification for SOCKS Version 5 specifies a 25 | generalized framework for the use of arbitrary authentication 26 | protocols in the initial socks connection setup. This document 27 | describes one of those protocols, as it fits into the SOCKS Version 5 28 | authentication "subnegotiation". 29 | 30 | Note: 31 | 32 | Unless otherwise noted, the decimal numbers appearing in packet- 33 | format diagrams represent the length of the corresponding field, in 34 | octets. Where a given octet must take on a specific value, the 35 | syntax X'hh' is used to denote the value of the single octet in that 36 | field. When the word 'Variable' is used, it indicates that the 37 | corresponding field has a variable length defined either by an 38 | associated (one or two octet) length field, or by a data type field. 39 | 40 | 2. Initial negotiation 41 | 42 | Once the SOCKS V5 server has started, and the client has selected the 43 | Username/Password Authentication protocol, the Username/Password 44 | subnegotiation begins. This begins with the client producing a 45 | Username/Password request: 46 | 47 | +----+------+----------+------+----------+ 48 | |VER | ULEN | UNAME | PLEN | PASSWD | 49 | +----+------+----------+------+----------+ 50 | | 1 | 1 | 1 to 255 | 1 | 1 to 255 | 51 | +----+------+----------+------+----------+ 52 | 53 | 54 | 55 | 56 | 57 | 58 | Leech Standards Track [Page 1] 59 | 60 | RFC 1929 Username Authentication for SOCKS V5 March 1996 61 | 62 | 63 | The VER field contains the current version of the subnegotiation, 64 | which is X'01'. The ULEN field contains the length of the UNAME field 65 | that follows. The UNAME field contains the username as known to the 66 | source operating system. The PLEN field contains the length of the 67 | PASSWD field that follows. The PASSWD field contains the password 68 | association with the given UNAME. 69 | 70 | The server verifies the supplied UNAME and PASSWD, and sends the 71 | following response: 72 | 73 | +----+--------+ 74 | |VER | STATUS | 75 | +----+--------+ 76 | | 1 | 1 | 77 | +----+--------+ 78 | 79 | A STATUS field of X'00' indicates success. If the server returns a 80 | `failure' (STATUS value other than X'00') status, it MUST close the 81 | connection. 82 | 83 | 3. Security Considerations 84 | 85 | This document describes a subnegotiation that provides authentication 86 | services to the SOCKS protocol. Since the request carries the 87 | password in cleartext, this subnegotiation is not recommended for 88 | environments where "sniffing" is possible and practical. 89 | 90 | 4. Author's Address 91 | 92 | Marcus Leech 93 | Bell-Northern Research Ltd 94 | P.O. Box 3511, Station C 95 | Ottawa, ON 96 | CANADA K1Y 4H7 97 | 98 | Phone: +1 613 763 9145 99 | EMail: mleech@bnr.ca 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | Leech Standards Track [Page 2] 115 | 116 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | 2 | #include "log.h" 3 | #include "util.h" 4 | 5 | // 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa 6 | // 61 61 61 61 61 61 61 61 61 61 61 aaaaaaa.... 7 | void memdump(uint8_t * in, int len) 8 | { 9 | int i = 0, j = 0; 10 | if (len > 128) 11 | len = 128; 12 | while (i < len) { 13 | printf("%02x ", in[i]); 14 | if (++i % 16 == 0) { 15 | putchar(' '); 16 | do { 17 | putchar(isprint(in[j]) ? in[j] : '.'); 18 | } while (++j < i); 19 | putchar('\n'); 20 | } else if (i % 4 == 0) { 21 | putchar(' '); 22 | } 23 | } 24 | int k = i % 16; 25 | if (k) { 26 | do { 27 | printf(" "); 28 | if (++k % 4 == 0) putchar(' '); 29 | } while (k < 16); 30 | do { 31 | putchar(isprint(in[j]) ? in[j] : '.'); 32 | } while (++j < i); 33 | putchar('\n'); 34 | } 35 | } 36 | 37 | // struct sockaddr_in --> "aaa.aaa.aaa.aaa:ppppp" 38 | char * addr2str(struct sockaddr_in * addr) 39 | { 40 | static char s[22]; 41 | sprintf(s, "%s:%hu", inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); 42 | return s; 43 | } 44 | 45 | // in_addr_t --> "aaa.aaa.aaa.aaa" 46 | char * inet_ntoaddr(void * addr) 47 | { 48 | return inet_ntoa(*(struct in_addr *)addr); 49 | } 50 | 51 | int create_and_listen(in_addr_t addr, in_port_t port) 52 | { 53 | int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 54 | if (fd == -1) { 55 | error("socket"); 56 | return 0; 57 | } 58 | struct sockaddr_in bnd_addr; 59 | memset(&bnd_addr, 0, sizeof(struct sockaddr_in)); 60 | bnd_addr.sin_family = AF_INET; 61 | bnd_addr.sin_port = port; 62 | bnd_addr.sin_addr.s_addr = addr; 63 | debug("bnd: %s", addr2str(&bnd_addr)); 64 | if (bind(fd, (struct sockaddr *)&bnd_addr, sizeof(struct sockaddr_in)) == -1) { 65 | error("bind"); 66 | info("bnd: %s", addr2str(&bnd_addr)); 67 | close(fd); 68 | return 0; 69 | } 70 | if (listen(fd, SOMAXCONN) == -1) { 71 | error("listen"); 72 | info("bnd: %s", addr2str(&bnd_addr)); 73 | close(fd); 74 | return 0; 75 | } 76 | return fd; 77 | } 78 | 79 | int create_and_connect(in_addr_t addr, in_port_t port) 80 | { 81 | int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 82 | if (fd == -1) { 83 | error("socket"); 84 | return 0; 85 | } 86 | struct sockaddr_in dst_addr; 87 | memset(&dst_addr, 0, sizeof(struct sockaddr_in)); 88 | dst_addr.sin_family = AF_INET; 89 | dst_addr.sin_port = port; 90 | dst_addr.sin_addr.s_addr = addr; 91 | debug("Connecting to %s...", addr2str(&dst_addr)); 92 | if (connect(fd, (struct sockaddr *)&dst_addr, sizeof(struct sockaddr_in)) == -1) { 93 | error("connect"); 94 | info("dst: %s", addr2str(&dst_addr)); 95 | close(fd); 96 | return 0; 97 | } 98 | debug("Connection established"); 99 | return fd; 100 | } 101 | 102 | in_addr_t resolve_domain(char * domain) 103 | { 104 | debug("Question: %s", domain); 105 | struct hostent * host = gethostbyname(domain); 106 | if (host == NULL) { 107 | error("gethostbyname"); 108 | info("Couldn't resolve host '%s'", domain); 109 | return 0; 110 | } 111 | debug("Answer: %s", inet_ntoaddr(host->h_addr_list[0])); 112 | return *(in_addr_t *)host->h_addr_list[0]; 113 | } 114 | 115 | void get_local_addr(int fd, in_addr_t * addr, in_port_t * port) 116 | { 117 | struct sockaddr_in local_addr; 118 | socklen_t local_addrlen = sizeof(struct sockaddr_in); 119 | getsockname(fd, (struct sockaddr *)&local_addr, &local_addrlen); 120 | debug("Local Address: %s", addr2str(&local_addr)); 121 | if (addr) *addr = local_addr.sin_addr.s_addr; 122 | if (port) *port = local_addr.sin_port; 123 | } 124 | 125 | void get_peer_addr(int fd, in_addr_t * addr, in_port_t * port) 126 | { 127 | struct sockaddr_in peer_addr; 128 | socklen_t peer_addrlen = sizeof(struct sockaddr_in); 129 | getpeername(fd, (struct sockaddr *)&peer_addr, &peer_addrlen); 130 | debug("Peer Address: %s", addr2str(&peer_addr)); 131 | if (addr) *addr = peer_addr.sin_addr.s_addr; 132 | if (port) *port = peer_addr.sin_port; 133 | } 134 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __UTIL_H__ 3 | #define __UTIL_H__ 4 | 5 | #include 6 | #include // exit() 7 | #include // uint8_t 8 | #include // bool, true, false 9 | #include // memset(), memcpy(), strcpy(), strerror() 10 | #include // isalpha(), isdigit(), isprint() 11 | #include // close() 12 | #include // socket(), bind(), listen(), accept(), recv(), send() 13 | #include // htons(), htonl(), inet_addr() 14 | #include // gethostbyname() 15 | #include 16 | 17 | void memdump(uint8_t * in, int len); 18 | char * addr2str(struct sockaddr_in * addr); 19 | int create_and_listen(in_addr_t addr, in_port_t port); 20 | int create_and_connect(in_addr_t addr, in_port_t port); 21 | in_addr_t resolve_domain(char * domain); 22 | void get_local_addr(int fd, in_addr_t * addr, in_port_t * port); 23 | void get_peer_addr(int fd, in_addr_t * addr, in_port_t * port); 24 | char * inet_ntoaddr(void * addr); 25 | 26 | #endif /* __UTIL_H__ */ 27 | --------------------------------------------------------------------------------