├── .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 |
17 |
18 |
19 |
20 |
21 |
22 |
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 |
--------------------------------------------------------------------------------