update README and Makefile for RcB2
[rofl0r-rocksocks5.git] / socksserver.c
blob5424d7e2019dd0c5aa4ccf93ba75a4c1a9e3713c
1 /*
2 Copyright (C) 2011-4ever rofl0r
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 // possible defines: (nothing) -> uses getaddrinfo,
20 // thus pulling in dependencies to malloc, free, gai_strerror et al, and even printf
21 // this increases the binary size by around 50 KB. supports all methods.
22 // NO_DNS_SUPPORT -> supports only method 0 (client submits ipv4). binary size is only ~25KB
23 // IPV4_ONLY -> uses gethostbyname for dns lookup.
24 // unfortunately most modern libcs just call getaddrinfo internally.
25 // IPV4_ONLY + USE_FIREDNS supports DNS via firedns. pulls dependecies to firedns.
26 // should still be much smaller than gethostbyname/getaddrinfo.
27 // NO_LOG - no output at all
28 // NO_IDSWITCH - do not compile possibility to switch uid/gid
29 // NO_DAEMONIZE - disable daemonize, which pulls in fork and other deps.
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <grp.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <arpa/inet.h>
38 #define USE_LIBULZ
40 #include "../rocksock/rocksockserver.h"
42 #include "../rocksock/endianness.h"
44 #include "../lib/include/stringptr.h"
45 #include "../lib/include/strlib.h"
46 #include "../lib/include/optparser.h"
47 #include "../lib/include/proclib.h"
49 #ifndef NO_LOG
50 #define CONFIG_LOG 1
51 #include "../lib/include/logger.h"
52 #define LOGP(X) log_perror(X)
53 #define LOGPUTS(F, X) log_puts(F, X)
54 #define LOGPUTD(F, X, Y) log_putd(F, X, Y)
55 #define LOGTS(X) log_timestamp(X)
56 #define LOGPUT(F, ...) log_put(F, __VA_ARGS__)
57 #else
58 #define CONFIG_LOG 0
59 #define LOGP(X) do {} while(0)
60 #define LOGPUTS(F, X) do {} while(0)
61 #define LOGPUTD(F, X, Y) do {} while(0)
62 #define LOGTS(X) do {} while(0)
63 #define LOGPUT(F, ...) do {} while(0)
64 #endif
66 #ifndef NO_DAEMONIZE
67 #define CONFIG_DAEMONIZE 1
68 #else
69 #define CONFIG_DAEMONIZE 0
70 #endif
72 #ifndef NO_IDSWITCH
73 #define CONFIG_IDSWITCH 1
74 #else
75 #define CONFIG_IDSWITCH 0
76 #endif
78 #ifndef IPV4_ONLY
79 #define CONFIG_IPV6 1
80 #ifdef USE_FIREDNS
81 #error USE_FIREDNS requires IPV4_ONLY too
82 #endif
83 #undef USE_FIREDNS
84 #else
85 #define CONFIG_IPV6 0
86 #endif
88 #ifdef USE_FIREDNS
89 #define CONFIG_FIREDNS 1
90 #include "../firedns/include/firedns.h"
91 static firedns_state fire;
92 static void init_firedns(void) {
93 firedns_init(&fire);
94 firedns_add_server(&fire, "8.8.4.4");
95 firedns_add_server(&fire, "8.8.8.8");
97 #else
98 #define CONFIG_FIREDNS 0
99 #define init_firedns() do {} while(0)
100 #define firedns_resolveip4(...) 0
101 #endif
103 #ifndef NO_DNS_SUPPORT
104 #define CONFIG_DNS 1
105 #else
106 #define CONFIG_DNS 0
107 #endif
109 #ifndef USER_BUFSIZE_KB
110 #define USER_BUFSIZE_KB 4
111 #endif
113 #ifndef USER_MAX_CONN
114 #define USER_MAX_CONN 32
115 #endif
117 #define CLIENT_BUFSIZE (USER_BUFSIZE_KB * 1024)
119 #ifndef MSG_NOSIGNAL
120 #define MSG_NOSIGNAL 0
121 #endif
123 typedef struct {
124 char* host;
125 unsigned short port;
126 struct addrinfo* hostaddr;
127 struct addrinfo hostaddr_buf;
128 } host_info;
130 typedef enum {
131 AM_NO_AUTH = 0,
132 AM_GSSAPI = 1,
133 AM_USERNAME = 2,
134 AM_INVALID = 0xFF
135 } rfc1928_authmethod;
137 typedef enum {
138 EC_SUCCESS = 0,
139 EC_GENERAL_FAILURE = 1,
140 EC_NOT_ALLOWED = 2,
141 EC_NET_UNREACHABLE = 3,
142 EC_HOST_UNREACHABLE = 4,
143 EC_CONN_REFUSED = 5,
144 EC_TTL_EXPIRED = 6,
145 EC_COMMAND_NOT_SUPPORTED = 7,
146 EC_ADDRESSTYPE_NOT_SUPPORTED = 8,
147 } rfc1928_errorcode;
149 typedef enum {
150 AT_IPV4 = 1,
151 AT_DOMAINNAME = 3,
152 AT_IPV6 = 4,
153 } rfc1928_atyp;
155 typedef enum {
156 BS_UNUSED = 0,
157 BS_IDLE,
158 BS_READING,
159 BS_WRITING
160 } bufstate;
162 typedef enum {
163 SS_DISCONNECTED = 0,
164 SS_SOCKSTARGET,
165 SS_CONNECTED,
166 SS_AWAITING_AUTH_PACKET,
167 SS_AWAITING_DISCONNECT,
168 SS_AUTHED,
169 SS_AWAITING_PIPE,
170 SS_WIRED
171 } socksstate;
173 typedef struct {
174 size_t start;
175 size_t used;
176 unsigned char buf[CLIENT_BUFSIZE];
177 bufstate state;
178 } socksbuffer;
180 typedef struct {
181 int target_fd;
182 socksbuffer* data;
183 socksstate state;
184 } fdinfo;
186 typedef struct {
187 rocksockserver serva;
188 stringptr username;
189 stringptr password;
190 char _username[256];
191 char _password[256];
192 socksbuffer clientbuffers[USER_MAX_CONN];
193 fdinfo clients[USER_MAX_CONN * 2];
194 rfc1928_authmethod accepted_authmethod;
195 int log;
196 } socksserver;
198 // dont waste buffers for stdin, out, err
199 #define MAX_FD (3 + (USER_MAX_CONN * 2))
200 #define fdindex(a) (a - 3)
202 static void logstart(void) {
203 LOGPUTS(1, SPL("["));
204 LOGTS(1);
205 LOGPUTS(1, SPL("] "));
208 static void printfd(int fd) {
209 LOGPUTS(1, SPLITERAL("("));
210 LOGPUTD(1, fd, 1);
211 LOGPUTS(1, SPLITERAL(")"));
214 static inline socksbuffer* find_free_buffer(socksserver* srv) {
215 size_t i;
216 for(i = 0; i < USER_MAX_CONN; i++) {
217 if(srv->clientbuffers[i].state == BS_UNUSED) return &srv->clientbuffers[i];
219 return NULL;
222 static int socksserver_write(socksserver* srv, int fd);
224 // forced == 1
225 static void socksserver_disconnect_client(socksserver* srv, int fd, int forced) {
226 fdinfo* client = &srv->clients[fdindex(fd)];
227 int fdflag = 0;
228 if(CONFIG_LOG && srv->log) {
229 logstart();
230 printfd(fd);
231 LOGPUT(1, VARISL(" disconnect, forced: "), VARII(forced), NULL);
234 if(forced) rocksockserver_disconnect_client(&srv->serva, fd);
235 client->state = SS_DISCONNECTED;
236 if(client->data) {
237 client->data->state = BS_UNUSED;
238 client->data->start = 0;
239 client->data->used = 0;
242 if(client->target_fd != -1) fdflag = 1;
243 fd = client->target_fd;
244 client->target_fd = -1;
246 if(fdflag) {
247 srv->clients[fdindex(fd)].target_fd = -1;
248 socksserver_disconnect_client(srv, fd, 1);
252 static int socksserver_on_clientdisconnect (void* userdata, int fd) {
253 socksserver* srv = (socksserver*) userdata;
254 // fdinfo* client = &srv->clients[fdindex(fd)];
255 //if(client->target_fd != -1) socksserver_disconnect_client(srv, client->target_fd, 0);
256 socksserver_disconnect_client(srv, fd, 0);
257 return 0;
260 static char* get_client_ip(struct sockaddr_storage* ip, char* buffer, size_t bufsize) {
261 if(CONFIG_IPV6) {
262 if(ip->ss_family == PF_INET)
263 return (char*) inet_ntop(PF_INET, &((struct sockaddr_in*) ip)->sin_addr, buffer, bufsize);
264 else return (char*) inet_ntop(PF_INET6, &((struct sockaddr_in6*) ip)->sin6_addr, buffer, bufsize);
265 } else {
266 if(bufsize >= 16)
267 stringfromipv4(((unsigned char*)(&((struct sockaddr_in*)ip)->sin_addr)), buffer);
268 else return NULL;
269 return buffer;
270 return NULL;
274 static int socksserver_on_clientconnect (void* userdata, struct sockaddr_storage* clientaddr, int fd) {
275 socksserver* srv = (socksserver*) userdata;
276 char buffer[256];
277 (void) buffer;
278 if(CONFIG_LOG && srv->log && clientaddr) {
279 logstart();
280 printfd(fd);
281 LOGPUT(1, VARISL(" connect from: "), VARIC(get_client_ip(clientaddr, buffer, sizeof(buffer))), NULL);
284 if(fd < 3 || fd >= MAX_FD) {
285 rocksockserver_disconnect_client(&srv->serva, fd);
286 return -2;
289 fdinfo* client = &srv->clients[fdindex(fd)];
291 // put into nonblocking mode, so that writes will not block the server
292 int flags = fcntl(fd, F_GETFL);
293 if(flags == -1) return -1;
294 if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) return -2;
296 client->data = find_free_buffer(srv);
297 if (!client->data) {
298 if(CONFIG_LOG && srv->log) {
299 logstart();
300 LOGPUTS(1, SPL("warning: couldnt find free buffer\n"));
302 rocksockserver_disconnect_client(&srv->serva, fd);
303 return -2;
306 client->state = SS_CONNECTED;
307 client->data->state = BS_IDLE;
308 client->data->start = 0;
309 client->target_fd = -1;
311 return 0;
314 static int socksserver_on_clientwantsdata (void* userdata, int fd) {
315 socksserver* srv = (socksserver*) userdata;
317 fdinfo* client = &srv->clients[fdindex(fd)];
319 if(client->target_fd >= 0 && srv->clients[fdindex(client->target_fd)].state == SS_AWAITING_PIPE)
320 srv->clients[fdindex(client->target_fd)].state = SS_WIRED;
322 if(client->data->state == BS_WRITING)
323 socksserver_write(srv, fd);
325 return 0;
328 // returns either the one authmethod supported by the server or AM_INVALID.
329 static rfc1928_authmethod socksserver_parse_authpacket(socksserver* srv, int fd) {
330 fdinfo* client = &srv->clients[fdindex(fd)];
331 unsigned char numMethods;
332 unsigned char i;
334 if(client->data->start < 3) return AM_INVALID;
335 if(client->data->buf[0] != 5) return AM_INVALID;
336 numMethods = client->data->buf[1];
337 for(i = 0; i < numMethods && (2U + i) < client->data->start; i++) {
338 if(client->data->buf[2 + i] == (unsigned char) srv->accepted_authmethod)
339 return srv->accepted_authmethod;
341 return AM_INVALID;
344 static int socksserver_read_client(socksserver* srv, int fd) {
345 fdinfo* client = &srv->clients[fdindex(fd)];
346 ssize_t nbytes;
348 if ((nbytes = recv(fd, client->data->buf + client->data->start, CLIENT_BUFSIZE - client->data->start, 0)) <= 0) {
349 socksserver_on_clientdisconnect(srv, fd);
350 rocksockserver_disconnect_client(&srv->serva, fd);
351 return -1;
353 client->data->start += nbytes;
354 client->data->used += nbytes;
355 return 0;
358 static int socksserver_write(socksserver* srv, int fd) {
359 fdinfo* client = &srv->clients[fdindex(fd)];
360 client->data->state = BS_WRITING;
361 ssize_t written = send(fd, client->data->buf + client->data->start, client->data->used - client->data->start, MSG_NOSIGNAL);
362 int err;
364 if (written < 0) {
365 err = errno;
366 if(err == EAGAIN || err == EWOULDBLOCK) return 0;
367 else {
368 //if(err == EBADF)
369 LOGP("writing");
370 socksserver_disconnect_client(srv, fd, 1);
371 rocksockserver_disconnect_client(&srv->serva, fd);
372 return 3;
374 } else if ((size_t) written == client->data->used - client->data->start)
375 client->data->state = BS_IDLE;
376 return 0;
380 +----+-----+-------+------+----------+----------+
381 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
382 +----+-----+-------+------+----------+----------+
383 | 1 | 1 | X'00' | 1 | Variable | 2 |
384 +----+-----+-------+------+----------+----------+
386 static int socksserver_send_error(socksserver* srv, int fd, rfc1928_errorcode ec) {
387 fdinfo* client = &srv->clients[fdindex(fd)];
388 size_t i = 0;
389 client->data->buf[i++] = 5;
390 client->data->buf[i++] = ec;
391 client->data->buf[i++] = 0;
393 client->data->buf[i++] = AT_IPV4;
394 client->data->buf[i++] = 0;
395 client->data->buf[i++] = 0;
396 client->data->buf[i++] = 0;
397 client->data->buf[i++] = 0;
399 client->data->buf[i++] = 0;
400 client->data->buf[i++] = 0;
402 client->data->used = i;
403 client->data->start = 0;
404 client->data->state = BS_WRITING;
405 return socksserver_write(srv, fd);
408 static int socksserver_send_auth_response_i(socksserver* srv, int fd, int version, rfc1928_authmethod meth) {
409 fdinfo* client = &srv->clients[fdindex(fd)];
410 client->data->buf[0] = version;
411 client->data->buf[1] = meth;
412 client->data->state = BS_WRITING;
413 client->data->start = 0;
414 client->data->used = 2;
415 return socksserver_write(srv, fd);
418 #define socksserver_send_auth_response(SRV, FD, METH) socksserver_send_auth_response_i(SRV, FD, 5, METH)
421 * rfc 1929
422 Once the SOCKS V5 server has started, and the client has selected the
423 Username/Password Authentication protocol, the Username/Password
424 subnegotiation begins. This begins with the client producing a
425 Username/Password request:
427 +----+------+----------+------+----------+
428 |VER | ULEN | UNAME | PLEN | PASSWD |
429 +----+------+----------+------+----------+
430 | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
431 +----+------+----------+------+----------+
433 The VER field contains the current version of the subnegotiation,
434 which is X'01'. The ULEN field contains the length of the UNAME field
435 that follows. The UNAME field contains the username as known to the
436 source operating system. The PLEN field contains the length of the
437 PASSWD field that follows. The PASSWD field contains the password
438 association with the given UNAME.
440 The server verifies the supplied UNAME and PASSWD, and sends the
441 following response:
443 +----+--------+
444 |VER | STATUS |
445 +----+--------+
446 | 1 | 1 |
447 +----+--------+
449 A STATUS field of X'00' indicates success. If the server returns a
450 `failure' (STATUS value other than X'00') status, it MUST close the
451 connection.
452 * */
454 // return 0 when packet is not complete, 1 if successfull, -1 if not
455 static int socksserver_check_credentials(socksserver* srv, int fd) {
456 fdinfo* client = &srv->clients[fdindex(fd)];
457 size_t i = 0;
458 unsigned char ulen, plen;
459 unsigned char* buf = client->data->buf;
460 if(client->data->start < 5) return 0;
461 if(buf[i++] != 1) return -1;
462 ulen = buf[i++];
463 if(client->data->start < i + ulen + 2) return 0; // passwd must be at least 1 char long
464 if(ulen != srv->username.size || memcmp(buf + i, srv->username.ptr, ulen)) return -1;
465 i += ulen;
466 plen = buf[i++];
467 if(client->data->start < i + plen) return 0;
468 if(plen != srv->password.size || memcmp(buf + i, srv->password.ptr, plen)) return -1;
469 return 1;
472 static inline uint16_t my_ntohs (unsigned char* port) {
473 #ifdef IS_LITTLE_ENDIAN
474 return (port[0] * 256) + port[1];
475 #else
476 return port[0] + (port[1] * 256);
477 #endif
480 static int resolve_host(host_info* hostinfo) {
481 if (!hostinfo || !hostinfo->host || !hostinfo->port) return -1;
482 if(CONFIG_IPV6) {
483 int ret;
484 struct addrinfo hints;
485 memset(&hints, 0, sizeof(hints));
486 hints.ai_family = PF_UNSPEC;
487 hints.ai_socktype = SOCK_STREAM;
488 ret = getaddrinfo(hostinfo->host, NULL, &hints, &hostinfo->hostaddr);
489 if(!ret) {
490 if(hostinfo->hostaddr->ai_addr->sa_family == PF_INET)
491 ((struct sockaddr_in*) hostinfo->hostaddr->ai_addr)->sin_port = htons(hostinfo->port);
492 else
493 ((struct sockaddr_in6*) hostinfo->hostaddr->ai_addr)->sin6_port = htons(hostinfo->port);
494 return 0;
495 } else
496 return ret;
497 } else {
498 struct in_addr *result;
499 struct hostent* he;
500 if(CONFIG_FIREDNS) {
501 result = firedns_resolveip4(&fire, hostinfo->host);
502 if(!result) return 1;
503 } else {
504 if (!(he = gethostbyname(hostinfo->host)) || !he->h_addr_list[0] || he->h_addrtype != AF_INET) return -2;
506 hostinfo->hostaddr->ai_family = AF_INET;
507 hostinfo->hostaddr->ai_addr->sa_family = AF_INET;
508 hostinfo->hostaddr->ai_addrlen = sizeof(struct sockaddr_in);
509 if(CONFIG_FIREDNS)
510 memcpy(&((struct sockaddr_in*) hostinfo->hostaddr->ai_addr)->sin_addr, result, 4);
511 else
512 memcpy(&((struct sockaddr_in*) hostinfo->hostaddr->ai_addr)->sin_addr, he->h_addr_list[0], 4);
513 ((struct sockaddr_in*) hostinfo->hostaddr->ai_addr)->sin_port = htons(hostinfo->port);
514 return 0;
519 The SOCKS request is formed as follows:
521 +----+-----+-------+------+----------+----------+
522 |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
523 +----+-----+-------+------+----------+----------+
524 | 1 | 1 | X'00' | 1 | Variable | 2 |
525 +----+-----+-------+------+----------+----------+
527 Where:
529 o VER protocol version: X'05'
530 o CMD
531 o CONNECT X'01'
532 o BIND X'02'
533 o UDP ASSOCIATE X'03'
534 o RSV RESERVED
535 o ATYP address type of following address
536 o IP V4 address: X'01'
537 o DOMAINNAME: X'03'
538 o IP V6 address: X'04'
539 o DST.ADDR desired destination address
540 o DST.PORT desired destination port in network octet
541 order
543 The SOCKS server will typically evaluate the request based on source
544 and destination addresses, and return one or more reply messages, as
545 appropriate for the request type.
547 In an address field (DST.ADDR, BND.ADDR), the ATYP field specifies
548 the type of address contained within the field:
550 o X'01'
552 the address is a version-4 IP address, with a length of 4 octets
554 o X'03'
556 the address field contains a fully-qualified domain name. The first
557 octet of the address field contains the number of octets of name that
558 follow, there is no terminating NUL octet.
560 o X'04'
562 the address is a version-6 IP address, with a length of 16 octets.
567 // parses the connect request and tries to establish the requested connection.
568 // returns -1 if packet is not complete
570 static int socksserver_connect_request(socksserver* srv, int fd) {
571 fdinfo* client = &srv->clients[fdindex(fd)];
572 size_t i = 0;
573 unsigned char dlen = 0;
574 unsigned char* buf = client->data->buf;
575 int flags, ret;
576 host_info addr;
578 struct addrinfo addrbuf;
579 struct sockaddr sockbuf;
581 memset(&addr, 0, sizeof(addr));
582 memset(&addrbuf, 0, sizeof(addrbuf));
583 memset(&sockbuf, 0, sizeof(sockbuf));
585 addrbuf.ai_addr = &sockbuf;
586 addr.hostaddr = &addrbuf;
589 if(!client->data->start) return -1;
590 if(buf[i++] != 5) return EC_NOT_ALLOWED; // check first byte whenever the message length is > 0 to not waste resources on maldoers
591 if(client->data->start < 1+1+1+1+4+2) return -1;
593 if(buf[i++] != 1) return EC_COMMAND_NOT_SUPPORTED; // we support only the connect method.
594 if(buf[i++] != 0) return EC_GENERAL_FAILURE;
595 switch(buf[i++]) {
596 case 1:
597 //ipv4
598 memcpy(&((struct sockaddr_in*) addr.hostaddr->ai_addr)->sin_addr, buf + 4, 4);
599 memcpy(&((struct sockaddr_in*) addr.hostaddr->ai_addr)->sin_port, buf + 8, 2);
600 ((struct sockaddr_in*) addr.hostaddr->ai_addr)->sin_family = PF_INET;
601 addr.hostaddr->ai_addr->sa_family = PF_INET;
602 addr.hostaddr->ai_addrlen = sizeof(struct sockaddr_in);
603 break;
604 case 3:
605 //dns
606 if(CONFIG_DNS) {
607 dlen = buf[i++];
608 if(client->data->start < 1U+1U+1U+1U+1U+dlen+2U) return -1;
609 addr.port = my_ntohs(buf + i + dlen);
610 buf[i + dlen] = 0;
611 addr.host = (char*) (buf + i);
612 if(CONFIG_IPV6) addr.hostaddr = NULL;
613 if(!resolve_host(&addr)) {
614 if(CONFIG_IPV6) {
615 memcpy(&addrbuf, addr.hostaddr, sizeof(struct addrinfo));
616 freeaddrinfo(addr.hostaddr);
617 addr.hostaddr = &addrbuf;
619 } else goto notsupported;
620 break;
621 } else goto notsupported;
622 case 4: //ipv6
623 if(CONFIG_IPV6) {
624 if(client->data->start < 1+1+1+1+16+2) return -1;
625 memcpy(&((struct sockaddr_in6*) addr.hostaddr->ai_addr)->sin6_addr, buf + 4, 16);
626 memcpy(&((struct sockaddr_in6*) addr.hostaddr->ai_addr)->sin6_port, buf + 20, 2);
627 ((struct sockaddr_in6*) addr.hostaddr->ai_addr)->sin6_family = PF_INET6;
628 addr.hostaddr->ai_addr->sa_family = PF_INET6;
629 addr.hostaddr->ai_addrlen = sizeof(struct sockaddr_in6);
630 break;
632 default:
633 notsupported:
634 return EC_ADDRESSTYPE_NOT_SUPPORTED;
636 client->target_fd = socket(addr.hostaddr->ai_addr->sa_family, SOCK_STREAM, 0);
637 if(client->target_fd == -1) {
638 neterror:
639 switch(errno) {
640 case ENETDOWN: case ENETUNREACH: case ENETRESET:
641 return EC_NET_UNREACHABLE;
642 case EHOSTUNREACH: case EHOSTDOWN:
643 return EC_HOST_UNREACHABLE;
644 case ECONNREFUSED:
645 return EC_CONN_REFUSED;
646 default:
647 return EC_GENERAL_FAILURE;
651 if(client->target_fd >= MAX_FD) {
652 close(client->target_fd);
653 return EC_GENERAL_FAILURE;
656 flags = fcntl(client->target_fd, F_GETFL);
657 if(flags == -1) return EC_GENERAL_FAILURE;
659 if(fcntl(client->target_fd, F_SETFL, flags | O_NONBLOCK) == -1) return EC_GENERAL_FAILURE;
661 ret = connect(client->target_fd, addr.hostaddr->ai_addr, addr.hostaddr->ai_addrlen);
662 if(ret == -1) {
663 ret = errno;
664 if (!(ret == EINPROGRESS || ret == EWOULDBLOCK))
665 goto neterror;
668 srv->clients[fdindex(client->target_fd)].state = SS_SOCKSTARGET;
669 srv->clients[fdindex(client->target_fd)].data = client->data;
670 srv->clients[fdindex(client->target_fd)].target_fd = fd;
671 rocksockserver_watch_fd(&srv->serva, client->target_fd);
673 if(CONFIG_LOG && srv->log) {
674 if(get_client_ip((struct sockaddr_storage*) addr.hostaddr->ai_addr, (char*) buf, CLIENT_BUFSIZE)) {
675 logstart();
676 printfd(fd);
677 LOGPUTS(1, SPLITERAL(" -> "));
678 printfd(client->target_fd);
679 LOGPUT(1, VARISL(" <"), VARIC((char*)buf), VARISL(">"), NULL);
683 return EC_SUCCESS;
687 The client connects to the server, and sends a version
688 identifier/method selection message:
690 +----+----------+----------+
691 |VER | NMETHODS | METHODS |
692 +----+----------+----------+
693 | 1 | 1 | 1 to 255 |
694 +----+----------+----------+
696 The VER field is set to X'05' for this version of the protocol. The
697 NMETHODS field contains the number of method identifier octets that
698 appear in the METHODS field.
700 The server selects from one of the methods given in METHODS, and
701 sends a METHOD selection message:
703 +----+--------+
704 |VER | METHOD |
705 +----+--------+
706 | 1 | 1 |
707 +----+--------+
709 If the selected METHOD is X'FF', none of the methods listed by the
710 client are acceptable, and the client MUST close the connection.
712 static int socksserver_on_clientread (void* userdata, int fd, size_t dummy) {
713 socksserver* srv = (socksserver*) userdata;
714 fdinfo* client = &srv->clients[fdindex(fd)];
715 rfc1928_authmethod authmethod;
716 char buf[4];
717 ssize_t readv;
718 int ret;
720 (void) dummy;
722 if(client->state == SS_AWAITING_PIPE || (client->data->state != BS_IDLE && client->data->state != BS_READING)) {
723 if((readv = recvfrom(fd, buf, 4, MSG_PEEK, NULL, NULL)) <= 0) {
724 if (!readv)
725 socksserver_on_clientdisconnect(userdata, fd);
726 else
727 LOGP("recvfrom");
728 rocksockserver_disconnect_client(&srv->serva, fd);
730 return 1;
733 if(client->state == SS_WIRED || client->state == SS_SOCKSTARGET) {
734 client->data->start = 0;
735 client->data->used = 0;
738 if(socksserver_read_client(srv, fd))
739 return 2;
741 switch(client->state) {
742 case SS_AWAITING_DISCONNECT:
743 socksserver_disconnect_client(srv, fd, 1);
744 break;
745 case SS_CONNECTED:
746 if((authmethod = socksserver_parse_authpacket(srv, fd)) != AM_INVALID) {
747 if(authmethod == AM_USERNAME)
748 client->state = SS_AWAITING_AUTH_PACKET;
749 else
750 client->state = SS_AUTHED;
751 } else {
752 client->state = SS_AWAITING_DISCONNECT;
754 socksserver_send_auth_response(srv, fd, authmethod);
755 break;
756 case SS_AWAITING_AUTH_PACKET:
757 ret = socksserver_check_credentials(srv, fd);
758 if (!ret) return 3;
759 if (ret == -1) {
760 client->state = SS_AWAITING_DISCONNECT;
761 socksserver_send_auth_response_i(srv, fd, 1, AM_INVALID);
762 } else {
763 client->state = SS_AUTHED;
764 socksserver_send_auth_response_i(srv, fd, 1, 0);
766 break;
767 case SS_AUTHED:
768 ret = socksserver_connect_request(srv, fd);
769 if(ret == -1) return 4;
770 if(ret) {
771 client->state = SS_AWAITING_DISCONNECT;
772 } else {
773 client->state = SS_AWAITING_PIPE;
775 socksserver_send_error(srv, fd, ret);
776 break;
777 case SS_SOCKSTARGET:
778 if(srv->clients[fdindex(client->target_fd)].state == SS_AWAITING_PIPE)
779 srv->clients[fdindex(client->target_fd)].state = SS_WIRED;
780 client->data->start = 0;
781 socksserver_write(srv, client->target_fd);
782 break;
783 case SS_WIRED:
784 client->data->start = 0;
785 socksserver_write(srv, client->target_fd);
786 break;
787 default:
788 break;
790 return 0;
793 static int socksserver_init(socksserver* srv, char* listenip, int port, int log, stringptr* username, stringptr* pass, int uid, int gid) {
794 memset(srv, 0, sizeof(socksserver));
795 srv->log = log;
796 if(rocksockserver_init(&srv->serva, listenip, port, (void*) srv)) return -1;
798 if(CONFIG_IDSWITCH) {
799 //dropping privs after bind()
800 if(gid != -1 && setgid(gid) == -1) LOGP("setgid");
801 if(gid != -1 && setgroups(0, NULL) == -1) LOGP("setgroups");
802 if(uid != -1 && setuid(uid) == -1) LOGP("setuid");
805 if(username->size) {
806 memcpy(srv->_username, username->ptr, username->size);
807 srv->username.ptr = srv->_username;
808 srv->username.size = username->size;
809 memset(username->ptr, 0, username->size);
812 if(pass->size) {
813 memcpy(srv->_password, pass->ptr, pass->size);
814 srv->password.ptr = srv->_password;
815 srv->password.size = pass->size;
816 memset(pass->ptr, 0, pass->size);
819 srv->accepted_authmethod = username->size ? AM_USERNAME : AM_NO_AUTH;
820 init_firedns();
822 if(rocksockserver_loop(&srv->serva, NULL, 0, &socksserver_on_clientconnect, &socksserver_on_clientread, &socksserver_on_clientwantsdata, &socksserver_on_clientdisconnect)) return -2;
823 return 0;
826 __attribute__ ((noreturn))
827 static void syntax(op_state* opt) {
828 LOGPUTS(1, SPL("progname -listenip=0.0.0.0 -port=1080 -log=0 -uid=0 -gid=0 -user=foo -pass=bar -d\n"));
829 LOGPUTS(1, SPL("user and pass are regarding socks authentication\n"));
830 LOGPUTS(1, SPL("passed options were:\n"));
831 if(CONFIG_LOG) op_printall(opt);
832 exit(1);
835 int main(int argc, char** argv) {
836 socksserver srv;
837 static const char defaultip[] = "127.0.0.1";
838 op_state opt_storage, *opt = &opt_storage;
839 op_init(opt, argc, argv);
840 SPDECLAREC(o_port, op_get(opt, SPL("port")));
841 SPDECLAREC(o_listenip, op_get(opt, SPL("listenip")));
843 int log;
844 if(CONFIG_LOG) {
845 SPDECLAREC(o_log, op_get(opt, SPL("log")));
846 log = o_log->size ? strtoint(o_log->ptr, o_log->size) : 1;
847 } else log = 0;
849 int uid, gid;
850 if(CONFIG_IDSWITCH) {
851 SPDECLAREC(o_uid, op_get(opt, SPL("uid")));
852 SPDECLAREC(o_gid, op_get(opt, SPL("gid")));
853 uid = o_uid->size ? strtoint(o_uid->ptr, o_uid->size) : -1;
854 gid = o_gid->size ? strtoint(o_gid->ptr, o_gid->size) : -1;
855 } else {
856 uid = -1;
857 gid = -1;
859 SPDECLAREC(o_user, op_get(opt, SPL("user")));
860 SPDECLAREC(o_pass, op_get(opt, SPL("pass")));
862 char* ip = o_listenip->size ? o_listenip->ptr : (char*) defaultip;
863 int port = o_port->size ? strtoint(o_port->ptr, o_port->size) : 1080;
865 if(CONFIG_LOG && op_hasflag(opt, SPLITERAL("-help"))) syntax(opt);
867 if((o_user->size && (!o_pass->size || o_user->size > 255)) || (o_pass->size && (!o_user->size || o_pass->size > 255))) {
868 LOGPUTS(1, SPL("fatal: username or password exceeding 255 chars, or only one of both set\n"));
869 return 1;
871 if(CONFIG_DAEMONIZE && op_hasflag(opt, SPL("d"))) daemonize();
872 socksserver_init(&srv, ip, port, log, o_user, o_pass, uid, gid);
874 return 0;