examples/portscanner: quick hack to make it work on single host/portrange
[rofl0r-rocksock.git] / rocksock.c
blob88e88cb6348fdd5d1f2e9cf1d8de704f48fe161c
1 /*
2 * author: rofl0r (C) 2011-2017
3 * License: LGPL 2.1+ with static linking exception
4 */
6 /*
7 * recognized defines: USE_SSL, ROCKSOCK_FILENAME, NO_DNS_SUPPORT
8 */
10 #undef _POSIX_C_SOURCE
11 #define _POSIX_C_SOURCE 200809L
12 #undef _GNU_SOURCE
13 #define _GNU_SOURCE
15 #include <string.h>
16 #include <errno.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <sys/select.h>
22 #include <netinet/in.h>
24 #ifndef SOCK_CLOEXEC
25 #warning compiling without SOCK_CLOEXEC support
26 #define SOCK_CLOEXEC 0
27 #endif
29 #include "rocksock.h"
30 #include "rocksock_internal.h"
31 #ifdef USE_LIBULZ_SYS
32 /* use this if you have installed libulz systemwide via make install */
33 #include <ulz/strlib.h>
34 #include <ulz/stdio-repl.h>
35 #elif defined(USE_LIBULZ)
36 /* use this if you want to use the extracted libulz sources via RcB2.
37 pass a -I flag pointing to its include dir, like so: -I ../lib/include
38 to RcB2 */
39 #include <strlib.h>
40 #include <stdio-repl.h>
41 #else
42 /* this version of ipv4fromstring was taken from libulz and tuned to be
43 more pedantic than the libulz version, so it can be used for isnumericipv4()
44 as well, as it strictly checks the input for correctness. */
45 static int ipv4fromstring(const char* ipstring, unsigned char* fourbytesptr) {
46 const char* start = ipstring;
47 size_t outbyte = 0;
48 while(outbyte < 4) {
49 if(*ipstring == '.' || !*ipstring) {
50 fourbytesptr[outbyte] = 0;
51 size_t b = 0;
52 unsigned tmp;
53 switch(ipstring - start) {
54 case 3:
55 tmp = (start[b++]-'0')*100;
56 if(tmp > 200) return 0;
57 fourbytesptr[outbyte] += tmp;
58 case 2:
59 fourbytesptr[outbyte] += (start[b++]-'0')*10;
60 case 1:
61 fourbytesptr[outbyte] += (start[b++]-'0');
62 break;
63 default:
64 return 0;
66 start = ipstring + 1;
67 outbyte++;
68 } else {
69 if(*ipstring < '0' || *ipstring > '9') return 0;
71 if(!*ipstring && outbyte < 4) return 0;
72 ipstring++;
74 if(ipstring[-1]) return 0;
75 return 1;
78 static int isnumericipv4(const char* ipstring) {
79 unsigned char ip[4];
80 return ipv4fromstring(ipstring, ip);
82 #endif
84 #ifndef ROCKSOCK_FILENAME
85 #define ROCKSOCK_FILENAME __FILE__
86 #endif
88 #ifndef MSG_NOSIGNAL
89 #define MSG_NOSIGNAL 0
90 #endif
92 #ifdef USE_SSL
93 #include "rocksock_ssl_internal.h"
94 #endif
96 int rocksock_seterror(rocksock* sock, rs_errorType errortype, int error, const char* file, int line) {
97 if (!sock) return RS_E_NULL;
98 sock->lasterror.errortype = errortype;
99 sock->lasterror.error = error;
100 sock->lasterror.line = line;
101 sock->lasterror.file = file;
102 sock->lasterror.failedProxy = -1;
103 return error;
106 #define MKOERR(S, X) rocksock_seterror(S, RS_ET_OWN, X, ROCKSOCK_FILENAME, __LINE__)
107 #define NOERR(S) rocksock_seterror(S, RS_ET_OWN, 0, NULL, 0)
108 #define MKSYSERR(S, X) rocksock_seterror(S, RS_ET_SYS, X, ROCKSOCK_FILENAME, __LINE__)
110 //#define NO_DNS_SUPPORT
111 static int rocksock_resolve_host(rocksock* sock, rs_hostInfo* hostinfo, rs_resolveStorage* result) {
112 if (!sock) return RS_E_NULL;
113 if (!hostinfo || !hostinfo->host[0] || !hostinfo->port) return MKOERR(sock, RS_E_NULL);
115 result->hostaddr = &(result->hostaddr_buf);
117 #ifndef NO_DNS_SUPPORT
118 struct addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_ADDRCONFIG};
119 int ret;
120 struct addrinfo *best, *save;
121 ret = getaddrinfo(hostinfo->host, NULL, &hints, &save);
122 if(!ret) {
123 best = save;
124 while(best->ai_addr->sa_family == AF_INET6 && best->ai_next) best = best->ai_next;
125 *result->hostaddr = *best;
126 result->hostaddr->ai_addr = (struct sockaddr*) &(result->hostaddr_aiaddr_buf);
127 result->hostaddr->ai_next = 0;
128 *result->hostaddr->ai_addr = *best->ai_addr;
130 if(result->hostaddr->ai_addr->sa_family == AF_INET)
131 ((struct sockaddr_in*) result->hostaddr->ai_addr)->sin_port = htons(hostinfo->port);
132 else
133 ((struct sockaddr_in6*) result->hostaddr->ai_addr)->sin6_port = htons(hostinfo->port);
134 freeaddrinfo(save);
135 return 0;
136 } else
137 return rocksock_seterror(sock, RS_ET_GAI, ret, ROCKSOCK_FILENAME, __LINE__);
138 #else
139 result->hostaddr->ai_addr = (struct sockaddr*) &(result->hostaddr_aiaddr_buf);
141 ((struct sockaddr_in*) result->hostaddr->ai_addr)->sin_port = htons(hostinfo->port);
142 ((struct sockaddr_in*) result->hostaddr->ai_addr)->sin_family = AF_INET;
143 result->hostaddr->ai_addr->sa_family = AF_INET;
144 result->hostaddr->ai_addrlen = sizeof(struct sockaddr_in);
145 ipv4fromstring(hostinfo->host, (unsigned char*) &((struct sockaddr_in*) result->hostaddr->ai_addr)->sin_addr);
147 return 0;
148 #endif
151 int rocksock_set_timeout(rocksock* sock, unsigned long timeout_millisec) {
152 if (!sock) return RS_E_NULL;
153 sock->timeout = timeout_millisec;
154 return NOERR(sock);
157 int rocksock_init(rocksock* sock, rs_proxy *proxies) {
158 if (!sock) return RS_E_NULL;
159 memset(sock, 0, sizeof(rocksock));
160 sock->lastproxy = -1;
161 sock->timeout = 60*1000;
162 sock->socket = -1;
163 sock->proxies = proxies;
164 return NOERR(sock);
167 static struct timeval* make_timeval(struct timeval* tv, unsigned long timeout) {
168 if(!tv) return NULL;
169 tv->tv_sec = timeout / 1000;
170 tv->tv_usec = 1000 * (timeout % 1000);
171 return tv;
174 static int do_connect(rocksock* sock, rs_resolveStorage* hostinfo, unsigned long timeout) {
175 int flags, ret;
176 fd_set wset;
177 struct timeval tv;
178 int optval;
179 socklen_t optlen = sizeof(optval);
181 sock->socket = socket(hostinfo->hostaddr->ai_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
182 if(sock->socket == -1) return MKSYSERR(sock, errno);
184 /* the socket has to be made non-blocking temporarily so we can enforce a connect timeout */
185 flags = fcntl(sock->socket, F_GETFL);
186 if(flags == -1) return MKSYSERR(sock, errno);
188 if(fcntl(sock->socket, F_SETFL, flags | O_NONBLOCK) == -1) return MKSYSERR(sock, errno);
190 ret = connect(sock->socket, hostinfo->hostaddr->ai_addr, hostinfo->hostaddr->ai_addrlen);
191 if(ret == -1) {
192 ret = errno;
193 if (!(ret == EINPROGRESS || ret == EWOULDBLOCK)) return MKSYSERR(sock, ret);
196 if(fcntl(sock->socket, F_SETFL, flags) == -1) return MKSYSERR(sock, errno);
198 FD_ZERO(&wset);
199 FD_SET(sock->socket, &wset);
201 ret = select(sock->socket+1, NULL, &wset, NULL, timeout ? make_timeval(&tv, timeout) : NULL);
203 if(ret == 1 && FD_ISSET(sock->socket, &wset)) {
204 ret = getsockopt(sock->socket, SOL_SOCKET, SO_ERROR, &optval,&optlen);
205 if(ret == -1) return MKSYSERR(sock, errno);
206 else if(optval) return MKSYSERR(sock, optval);
207 return 0;
208 } else if(ret == 0) return MKOERR(sock, RS_E_HIT_CONNECTTIMEOUT);
210 return MKSYSERR(sock, errno);
213 static int rocksock_setup_socks4_header(rocksock* sock, int is4a, char* buffer, rs_proxy* proxy, size_t* bytesused) {
214 int ret;
215 buffer[0] = 4;
216 buffer[1] = 1;
217 buffer[2] = proxy->hostinfo.port / 256;
218 buffer[3] = proxy->hostinfo.port % 256;
220 if(is4a) {
221 buffer[4] = 0;
222 buffer[5] = 0;
223 buffer[6] = 0;
224 buffer[7] = 1;
225 } else {
226 rs_resolveStorage stor;
227 ret = rocksock_resolve_host(sock, &proxy->hostinfo, &stor);
228 if(ret) return ret;
229 if(stor.hostaddr->ai_family != AF_INET)
230 return MKOERR(sock, RS_E_SOCKS4_NO_IP6);
231 buffer[4] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[0];
232 buffer[5] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[1];
233 buffer[6] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[2];
234 buffer[7] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[3];
236 buffer[8] = 0;
237 *bytesused = 9;
238 if(is4a) {
239 char *p = buffer + *bytesused;
240 size_t l = strlen(proxy->hostinfo.host) + 1;
241 /* memcpy is safe because all functions accepting a hostname check it's < 255 */
242 memcpy(p, proxy->hostinfo.host, l);
243 *bytesused += l;
245 return NOERR(sock);
248 int rocksock_connect(rocksock* sock, const char* host, unsigned short port, int useSSL) {
249 ptrdiff_t px;
250 int ret, trysocksv4a;
251 rs_hostInfo targethost;
252 rs_hostInfo* connector;
253 rs_proxy dummy;
254 rs_proxy* targetproxy;
255 char socksdata[768];
256 char* p;
257 size_t socksused = 0, bytes;
258 if (!sock) return RS_E_NULL;
259 if (!host || !port)
260 return MKOERR(sock, RS_E_NULL);
261 size_t hl = strlen(host);
262 if(hl > 255)
263 return MKOERR(sock, RS_E_HOSTNAME_TOO_LONG);
264 #ifndef USE_SSL
265 if (useSSL) return MKOERR(sock, RS_E_NO_SSL);
266 #endif
267 memcpy(targethost.host, host, hl+1);
268 targethost.port = port;
270 if(sock->lastproxy >= 0)
271 connector = &sock->proxies[0].hostinfo;
272 else
273 connector = &targethost;
275 rs_resolveStorage stor;
277 ret = rocksock_resolve_host(sock, connector, &stor);
278 if(ret) {
279 check_proxy0_failure:
280 px = -1;
281 goto proxyfailure;
284 ret = do_connect(sock, &stor, sock->timeout);
285 if(ret) goto check_proxy0_failure;
287 for(px = 0; px <= sock->lastproxy; px++) {
288 if(px == sock->lastproxy) {
289 targetproxy = &dummy;
290 dummy.hostinfo = targethost;
291 dummy.password[0] = 0;
292 dummy.username[0] = 0;
293 dummy.proxytype = RS_PT_NONE;
294 } else {
295 targetproxy = &sock->proxies[px + 1];
297 // send socks connection data
298 switch(sock->proxies[px].proxytype) {
299 case RS_PT_SOCKS4:
300 trysocksv4a = 1;
301 trysocks4:
302 ret = rocksock_setup_socks4_header(sock, trysocksv4a, socksdata, targetproxy, &socksused);
303 if(ret) {
304 proxyfailure:
305 sock->lasterror.failedProxy = px == sock->lastproxy ? -1 : px + 1;
306 if(sock->socket != -1) {
307 close(sock->socket);
308 sock->socket = -1;
310 return ret;
312 ret = rocksock_send(sock, socksdata, socksused, 0, &bytes);
313 if(ret) goto proxyfailure;
314 ret = rocksock_recv(sock, socksdata, 8, 8, &bytes);
315 if(ret) goto proxyfailure;
316 if(bytes < 8 || socksdata[0] != 0) {
317 err_unexpected:
318 ret = MKOERR(sock, RS_E_PROXY_UNEXPECTED_RESPONSE);
319 goto proxyfailure;
321 switch(socksdata[1]) {
322 case 0x5a:
323 break;
324 case 0x5b:
325 if(trysocksv4a) {
326 trysocksv4a = 0;
327 goto trysocks4;
329 err_proxyconnect:
330 ret = MKOERR(sock, RS_E_TARGETPROXY_CONNECT_FAILED);
331 goto proxyfailure;
332 case 0x5c: case 0x5d:
333 err_proxyauth:
334 ret = MKOERR(sock, RS_E_PROXY_AUTH_FAILED);
335 goto proxyfailure;
336 default:
337 goto err_unexpected;
339 break;
340 case RS_PT_SOCKS5:
341 p = socksdata;
342 *p++ = 5;
343 if(sock->proxies[px].username[0] && sock->proxies[px].password[0]) {
344 *p++ = 2;
345 *p++ = 0;
346 *p++ = 2;
347 } else {
348 *p++ = 1;
349 *p++ = 0;
351 bytes = p - socksdata;
352 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
353 if(ret) goto proxyfailure;
354 ret = rocksock_recv(sock, socksdata, 2, 2, &bytes);
355 if(ret) goto proxyfailure;
356 if(bytes < 2 || socksdata[0] != 5) goto err_unexpected;
357 if(socksdata[1] == '\xff') {
358 goto err_proxyauth;
359 } else if (socksdata[1] == 2) {
360 if(sock->proxies[px].username[0] && sock->proxies[px].password[0]) {
362 +----+------+----------+------+----------+
363 |VER | ULEN | UNAME | PLEN | PASSWD |
364 +----+------+----------+------+----------+
365 | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
366 +----+------+----------+------+----------+
368 p = socksdata;
369 *p++ = 1;
370 bytes = strlen(sock->proxies[px].username);
371 *p++ = bytes;
372 memcpy(p, sock->proxies[px].username, bytes);
373 p += bytes;
374 bytes = strlen(sock->proxies[px].password);
375 *p++ = bytes;
376 memcpy(p, sock->proxies[px].password, bytes);
377 p += bytes;
378 bytes = p - socksdata;
379 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
380 if(ret) goto proxyfailure;
381 ret = rocksock_recv(sock, socksdata, 2, 2, &bytes);
382 if(ret) goto proxyfailure;
383 if(bytes < 2) goto err_unexpected;
384 else if(socksdata[1] != 0) goto err_proxyauth;
385 } else {
386 goto err_proxyauth;
389 p = socksdata;
390 *p++ = 5;
391 *p++ = 1;
392 *p++ = 0;
393 if(isnumericipv4(targetproxy->hostinfo.host)) {
394 *p++ = 1; // ipv4 method
395 bytes = 4;
396 ipv4fromstring(targetproxy->hostinfo.host, (unsigned char*) p);
397 } else {
398 *p++ = 3; //hostname method, requires the server to do dns lookups.
399 bytes = strlen(targetproxy->hostinfo.host);
400 if(bytes > 255)
401 return MKOERR(sock, RS_E_SOCKS5_AUTH_EXCEEDSIZE);
402 *p++ = bytes;
403 memcpy(p, targetproxy->hostinfo.host, bytes);
405 p+=bytes;
406 *p++ = targetproxy->hostinfo.port / 256;
407 *p++ = targetproxy->hostinfo.port % 256;
408 bytes = p - socksdata;
409 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
410 if(ret) goto proxyfailure;
411 ret = rocksock_recv(sock, socksdata, sizeof(socksdata), sizeof(socksdata), &bytes);
412 if(ret) goto proxyfailure;
413 if(bytes < 2) goto err_unexpected;
414 switch(socksdata[1]) {
415 case 0:
416 break;
417 case 1:
418 ret = MKOERR(sock, RS_E_PROXY_GENERAL_FAILURE);
419 goto proxyfailure;
420 case 2:
421 goto err_proxyauth;
422 case 3:
423 ret = MKOERR(sock, RS_E_TARGETPROXY_NET_UNREACHABLE);
424 goto proxyfailure;
425 case 4:
426 ret = MKOERR(sock, RS_E_TARGETPROXY_HOST_UNREACHABLE);
427 goto proxyfailure;
428 case 5:
429 ret = MKOERR(sock, RS_E_TARGETPROXY_CONN_REFUSED);
430 goto proxyfailure;
431 case 6:
432 ret = MKOERR(sock, RS_E_TARGETPROXY_TTL_EXPIRED);
433 goto proxyfailure;
434 case 7:
435 ret = MKOERR(sock, RS_E_PROXY_COMMAND_NOT_SUPPORTED);
436 goto proxyfailure;
437 case 8:
438 ret = MKOERR(sock, RS_E_PROXY_ADDRESSTYPE_NOT_SUPPORTED);
439 goto proxyfailure;
440 default:
441 goto err_unexpected;
443 break;
444 case RS_PT_HTTP:
445 bytes = snprintf(socksdata, sizeof(socksdata), "CONNECT %s:%d HTTP/1.1\r\n\r\n", targetproxy->hostinfo.host, targetproxy->hostinfo.port);
446 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
447 if(ret) goto proxyfailure;
448 ret = rocksock_recv(sock, socksdata, sizeof(socksdata), sizeof(socksdata), &bytes);
449 if(ret) goto proxyfailure;
450 if(bytes < 12) goto err_unexpected;
451 if(socksdata[9] != '2') goto err_proxyconnect;
452 break;
453 default:
454 break;
458 #ifdef USE_SSL
459 if(useSSL) {
460 ret = rocksock_ssl_connect_fd(sock);
461 if(ret) return ret;
463 #endif
464 return NOERR(sock);
467 typedef enum {
468 RS_OT_SEND = 0,
469 RS_OT_READ
470 } rs_operationType;
472 static int rocksock_operation(rocksock* sock, rs_operationType operation, char* buffer, size_t bufsize, size_t chunksize, size_t* bytes) {
473 if (!sock) return RS_E_NULL;
474 if (!buffer || !bytes || (!bufsize && operation == RS_OT_READ)) return MKOERR(sock, RS_E_NULL);
475 *bytes = 0;
476 struct timeval tv;
477 fd_set fd;
478 fd_set* rfd = NULL;
479 fd_set* wfd = NULL;
480 int ret = 0;
481 size_t bytesleft = bufsize ? bufsize : strlen(buffer);
482 size_t byteswanted;
483 char* bufptr = buffer;
485 if (sock->socket == -1) return MKOERR(sock, RS_E_NO_SOCKET);
486 if(operation == RS_OT_SEND) wfd = &fd;
487 else rfd = &fd;
489 if(sock->timeout) {
490 if(operation == RS_OT_SEND)
491 ret = setsockopt(sock->socket, SOL_SOCKET, SO_SNDTIMEO, (void*) make_timeval(&tv, sock->timeout), sizeof(tv));
492 else
493 ret = setsockopt(sock->socket, SOL_SOCKET, SO_RCVTIMEO, (void*) make_timeval(&tv, sock->timeout), sizeof(tv));
496 if (ret == -1) return MKSYSERR(sock, errno);
498 while(bytesleft) {
499 byteswanted = (chunksize && chunksize < bytesleft) ? chunksize : bytesleft;
500 #ifdef USE_SSL
501 if (sock->ssl) {
502 if(operation == RS_OT_SEND)
503 ret = rocksock_ssl_send(sock, bufptr, byteswanted);
504 else
505 ret = rocksock_ssl_recv(sock, bufptr, byteswanted);
506 } else {
507 #endif
508 /* enforce the timeout by using select() before doing the actual recv/send */
509 FD_SET(sock->socket, &fd);
510 ret=select(sock->socket+1, rfd, wfd, NULL, sock->timeout ? make_timeval(&tv, sock->timeout) : NULL);
511 if(!FD_ISSET(sock->socket, &fd)) MKOERR(sock, RS_E_NULL); // temp test
512 if(ret == -1) {
513 //printf("h: %s, skt: %d, to: %d:%d\n", targethost.host, sock->socket, tv.tv_sec, tv.tv_usec);
514 return MKSYSERR(sock, errno);
516 else if(!ret) return MKOERR(sock, RS_OT_READ ? RS_E_HIT_READTIMEOUT : RS_E_HIT_WRITETIMEOUT);
518 if(operation == RS_OT_SEND)
519 ret = send(sock->socket, bufptr, byteswanted, MSG_NOSIGNAL);
520 else
521 ret = recv(sock->socket, bufptr, byteswanted, 0);
523 #ifdef USE_SSL
525 #endif
527 if(!ret) // The return value will be 0 when the peer has performed an orderly shutdown.
528 return MKOERR(sock, RS_E_REMOTE_DISCONNECTED);
529 else if(ret == -1) {
530 ret = errno;
531 if(ret == EWOULDBLOCK || ret == EINPROGRESS) return MKOERR(sock, RS_OT_READ ? RS_E_HIT_READTIMEOUT : RS_E_HIT_WRITETIMEOUT);
532 return MKSYSERR(sock, errno);
535 bytesleft -= ret;
536 bufptr += ret;
537 *bytes += ret;
538 if(operation == RS_OT_READ && (size_t) ret < byteswanted) break;
540 return NOERR(sock);
543 int rocksock_send(rocksock* sock, char* buffer, size_t bufsize, size_t chunksize, size_t* byteswritten) {
544 return rocksock_operation(sock, RS_OT_SEND, buffer, bufsize, chunksize, byteswritten);
547 int rocksock_recv(rocksock* sock, char* buffer, size_t bufsize, size_t chunksize, size_t* bytesread) {
548 return rocksock_operation(sock, RS_OT_READ, buffer, bufsize, chunksize, bytesread);
551 int rocksock_disconnect(rocksock* sock) {
552 if (!sock) return RS_E_NULL;
553 #ifdef USE_SSL
554 rocksock_ssl_free_context(sock);
555 #endif
556 if(sock->socket != -1) close(sock->socket);
557 sock->socket = -1;
558 return NOERR(sock);
561 int rocksock_clear(rocksock* sock) {
562 if (!sock) return RS_E_NULL;
563 sock->lastproxy = -1;
564 sock->proxies = 0;
565 return NOERR(sock);