Makefile: build 2 more examples
[rofl0r-rocksock.git] / rocksock.c
blob8786127c2d0a8c1f68e59ba467eb83b25887d030
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
32 //RcB: SKIPUON "USE_LIBULZ"
33 #include <ulz/strlib.h>
34 #include <ulz/stdio-repl.h>
35 //RcB: SKIPUOFF "USE_LIBULZ"
36 #else
37 /* this version of ipv4fromstring was taken from libulz and tuned to be
38 more pedantic than the libulz version, so it can be used for isnumericipv4()
39 as well, as it strictly checks the input for correctness. */
40 static int ipv4fromstring(const char* ipstring, unsigned char* fourbytesptr) {
41 const char* start = ipstring;
42 size_t outbyte = 0;
43 while(outbyte < 4) {
44 if(*ipstring == '.' || !*ipstring) {
45 fourbytesptr[outbyte] = 0;
46 size_t b = 0;
47 unsigned tmp;
48 switch(ipstring - start) {
49 case 3:
50 tmp = (start[b++]-'0')*100;
51 if(tmp > 200) return 0;
52 fourbytesptr[outbyte] += tmp;
53 case 2:
54 fourbytesptr[outbyte] += (start[b++]-'0')*10;
55 case 1:
56 fourbytesptr[outbyte] += (start[b++]-'0');
57 break;
58 default:
59 return 0;
61 start = ipstring + 1;
62 outbyte++;
63 } else {
64 if(*ipstring < '0' || *ipstring > '9') return 0;
66 if(!*ipstring && outbyte < 4) return 0;
67 ipstring++;
69 if(ipstring[-1]) return 0;
70 return 1;
73 static int isnumericipv4(const char* ipstring) {
74 unsigned char ip[4];
75 return ipv4fromstring(ipstring, ip);
77 #endif
79 #ifndef ROCKSOCK_FILENAME
80 #define ROCKSOCK_FILENAME __FILE__
81 #endif
83 #ifndef MSG_NOSIGNAL
84 #define MSG_NOSIGNAL 0
85 #endif
87 #ifdef USE_SSL
88 #include "rocksock_ssl_internal.h"
89 #endif
91 int rocksock_seterror(rocksock* sock, rs_errorType errortype, int error, const char* file, int line) {
92 if (!sock) return RS_E_NULL;
93 sock->lasterror.errortype = errortype;
94 sock->lasterror.error = error;
95 sock->lasterror.line = line;
96 sock->lasterror.file = file;
97 sock->lasterror.failedProxy = -1;
98 return error;
101 #define MKOERR(S, X) rocksock_seterror(S, RS_ET_OWN, X, ROCKSOCK_FILENAME, __LINE__)
102 #define NOERR(S) rocksock_seterror(S, RS_ET_OWN, 0, NULL, 0)
103 #define MKSYSERR(S, X) rocksock_seterror(S, RS_ET_SYS, X, ROCKSOCK_FILENAME, __LINE__)
105 //#define NO_DNS_SUPPORT
106 static int rocksock_resolve_host(rocksock* sock, rs_hostInfo* hostinfo, rs_resolveStorage* result) {
107 if (!sock) return RS_E_NULL;
108 if (!hostinfo || !hostinfo->host[0] || !hostinfo->port) return MKOERR(sock, RS_E_NULL);
110 result->hostaddr = &(result->hostaddr_buf);
112 #ifndef NO_DNS_SUPPORT
113 struct addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_ADDRCONFIG};
114 int ret;
115 struct addrinfo *best, *save;
116 ret = getaddrinfo(hostinfo->host, NULL, &hints, &save);
117 if(!ret) {
118 best = save;
119 while(best->ai_addr->sa_family == AF_INET6 && best->ai_next) best = best->ai_next;
120 *result->hostaddr = *best;
121 result->hostaddr->ai_addr = (struct sockaddr*) &(result->hostaddr_aiaddr_buf);
122 result->hostaddr->ai_next = 0;
123 *result->hostaddr->ai_addr = *best->ai_addr;
125 if(result->hostaddr->ai_addr->sa_family == AF_INET)
126 ((struct sockaddr_in*) result->hostaddr->ai_addr)->sin_port = htons(hostinfo->port);
127 else
128 ((struct sockaddr_in6*) result->hostaddr->ai_addr)->sin6_port = htons(hostinfo->port);
129 freeaddrinfo(save);
130 return 0;
131 } else
132 return rocksock_seterror(sock, RS_ET_GAI, ret, ROCKSOCK_FILENAME, __LINE__);
133 #else
134 result->hostaddr->ai_addr = (struct sockaddr*) &(result->hostaddr_aiaddr_buf);
136 ((struct sockaddr_in*) result->hostaddr->ai_addr)->sin_port = htons(hostinfo->port);
137 ((struct sockaddr_in*) result->hostaddr->ai_addr)->sin_family = AF_INET;
138 result->hostaddr->ai_addr->sa_family = AF_INET;
139 result->hostaddr->ai_addrlen = sizeof(struct sockaddr_in);
140 ipv4fromstring(hostinfo->host, (unsigned char*) &((struct sockaddr_in*) result->hostaddr->ai_addr)->sin_addr);
142 return 0;
143 #endif
146 int rocksock_set_timeout(rocksock* sock, unsigned long timeout_millisec) {
147 if (!sock) return RS_E_NULL;
148 sock->timeout = timeout_millisec;
149 return NOERR(sock);
152 int rocksock_init(rocksock* sock, rs_proxy *proxies) {
153 if (!sock) return RS_E_NULL;
154 memset(sock, 0, sizeof(rocksock));
155 sock->lastproxy = -1;
156 sock->timeout = 60*1000;
157 sock->socket = -1;
158 sock->proxies = proxies;
159 return NOERR(sock);
162 static struct timeval* make_timeval(struct timeval* tv, unsigned long timeout) {
163 if(!tv) return NULL;
164 tv->tv_sec = timeout / 1000;
165 tv->tv_usec = 1000 * (timeout % 1000);
166 return tv;
169 static int do_connect(rocksock* sock, rs_resolveStorage* hostinfo, unsigned long timeout) {
170 int flags, ret;
171 fd_set wset;
172 struct timeval tv;
173 int optval;
174 socklen_t optlen = sizeof(optval);
176 sock->socket = socket(hostinfo->hostaddr->ai_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
177 if(sock->socket == -1) return MKSYSERR(sock, errno);
179 /* the socket has to be made non-blocking temporarily so we can enforce a connect timeout */
180 flags = fcntl(sock->socket, F_GETFL);
181 if(flags == -1) return MKSYSERR(sock, errno);
183 if(fcntl(sock->socket, F_SETFL, flags | O_NONBLOCK) == -1) return errno;
185 ret = connect(sock->socket, hostinfo->hostaddr->ai_addr, hostinfo->hostaddr->ai_addrlen);
186 if(ret == -1) {
187 ret = errno;
188 if (!(ret == EINPROGRESS || ret == EWOULDBLOCK)) return MKSYSERR(sock, ret);
191 if(fcntl(sock->socket, F_SETFL, flags) == -1) return MKSYSERR(sock, errno);
193 FD_ZERO(&wset);
194 FD_SET(sock->socket, &wset);
196 ret = select(sock->socket+1, NULL, &wset, NULL, timeout ? make_timeval(&tv, timeout) : NULL);
198 if(ret == 1 && FD_ISSET(sock->socket, &wset)) {
199 ret = getsockopt(sock->socket, SOL_SOCKET, SO_ERROR, &optval,&optlen);
200 if(ret == -1) return MKSYSERR(sock, errno);
201 else if(optval) return MKSYSERR(sock, optval);
202 return 0;
203 } else if(ret == 0) return MKOERR(sock, RS_E_HIT_CONNECTTIMEOUT);
205 return MKSYSERR(sock, errno);
208 static int rocksock_setup_socks4_header(rocksock* sock, int is4a, char* buffer, rs_proxy* proxy, size_t* bytesused) {
209 int ret;
210 buffer[0] = 4;
211 buffer[1] = 1;
212 buffer[2] = proxy->hostinfo.port / 256;
213 buffer[3] = proxy->hostinfo.port % 256;
215 if(is4a) {
216 buffer[4] = 0;
217 buffer[5] = 0;
218 buffer[6] = 0;
219 buffer[7] = 1;
220 } else {
221 rs_resolveStorage stor;
222 ret = rocksock_resolve_host(sock, &proxy->hostinfo, &stor);
223 if(ret) return ret;
224 if(stor.hostaddr->ai_family != AF_INET)
225 return MKOERR(sock, RS_E_SOCKS4_NO_IP6);
226 buffer[4] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[0];
227 buffer[5] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[1];
228 buffer[6] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[2];
229 buffer[7] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[3];
231 buffer[8] = 0;
232 *bytesused = 9;
233 if(is4a) {
234 char *p = buffer + *bytesused;
235 size_t l = strlen(proxy->hostinfo.host) + 1;
236 /* memcpy is safe because all functions accepting a hostname check it's < 255 */
237 memcpy(p, proxy->hostinfo.host, l);
238 *bytesused += l;
240 return NOERR(sock);
243 int rocksock_connect(rocksock* sock, const char* host, unsigned short port, int useSSL) {
244 ptrdiff_t px;
245 int ret, trysocksv4a;
246 rs_hostInfo targethost;
247 rs_hostInfo* connector;
248 rs_proxy dummy;
249 rs_proxy* targetproxy;
250 char socksdata[768];
251 char* p;
252 size_t socksused = 0, bytes;
253 if (!sock) return RS_E_NULL;
254 if (!host || !port)
255 return MKOERR(sock, RS_E_NULL);
256 size_t hl = strlen(host);
257 if(hl > 255)
258 return MKOERR(sock, RS_E_HOSTNAME_TOO_LONG);
259 #ifndef USE_SSL
260 if (useSSL) return MKOERR(sock, RS_E_NO_SSL);
261 #endif
262 memcpy(targethost.host, host, hl+1);
263 targethost.port = port;
265 if(sock->lastproxy >= 0)
266 connector = &sock->proxies[0].hostinfo;
267 else
268 connector = &targethost;
270 rs_resolveStorage stor;
272 ret = rocksock_resolve_host(sock, connector, &stor);
273 if(ret) {
274 check_proxy0_failure:
275 if(sock->lastproxy >= 0) sock->lasterror.failedProxy = 0;
276 return ret;
279 ret = do_connect(sock, &stor, sock->timeout);
280 if(ret) goto check_proxy0_failure;
282 for(px = 0; px <= sock->lastproxy; px++) {
283 if(px == sock->lastproxy) {
284 targetproxy = &dummy;
285 dummy.hostinfo = targethost;
286 dummy.password[0] = 0;
287 dummy.username[0] = 0;
288 dummy.proxytype = RS_PT_NONE;
289 } else {
290 targetproxy = &sock->proxies[px + 1];
292 // send socks connection data
293 switch(sock->proxies[px].proxytype) {
294 case RS_PT_SOCKS4:
295 trysocksv4a = 1;
296 trysocks4:
297 ret = rocksock_setup_socks4_header(sock, trysocksv4a, socksdata, targetproxy, &socksused);
298 if(ret) {
299 proxyfailure:
300 sock->lasterror.failedProxy = px;
301 return ret;
303 ret = rocksock_send(sock, socksdata, socksused, 0, &bytes);
304 if(ret) goto proxyfailure;
305 ret = rocksock_recv(sock, socksdata, 8, 8, &bytes);
306 if(ret) goto proxyfailure;
307 if(bytes < 8 || socksdata[0] != 0) {
308 err_unexpected:
309 ret = MKOERR(sock, RS_E_PROXY_UNEXPECTED_RESPONSE);
310 goto proxyfailure;
312 switch(socksdata[1]) {
313 case 0x5a:
314 break;
315 case 0x5b:
316 if(trysocksv4a) {
317 trysocksv4a = 0;
318 goto trysocks4;
320 err_proxyconnect:
321 ret = MKOERR(sock, RS_E_TARGETPROXY_CONNECT_FAILED);
322 goto proxyfailure;
323 case 0x5c: case 0x5d:
324 err_proxyauth:
325 ret = MKOERR(sock, RS_E_PROXY_AUTH_FAILED);
326 goto proxyfailure;
327 default:
328 goto err_unexpected;
330 break;
331 case RS_PT_SOCKS5:
332 p = socksdata;
333 *p++ = 5;
334 if(sock->proxies[px].username[0] && sock->proxies[px].password[0]) {
335 *p++ = 2;
336 *p++ = 0;
337 *p++ = 2;
338 } else {
339 *p++ = 1;
340 *p++ = 0;
342 bytes = p - socksdata;
343 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
344 if(ret) goto proxyfailure;
345 ret = rocksock_recv(sock, socksdata, 2, 2, &bytes);
346 if(ret) goto proxyfailure;
347 if(bytes < 2 || socksdata[0] != 5) goto err_unexpected;
348 if(socksdata[1] == '\xff') {
349 goto err_proxyauth;
350 } else if (socksdata[1] == 2) {
351 if(sock->proxies[px].username[0] && sock->proxies[px].password[0]) {
353 +----+------+----------+------+----------+
354 |VER | ULEN | UNAME | PLEN | PASSWD |
355 +----+------+----------+------+----------+
356 | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
357 +----+------+----------+------+----------+
359 p = socksdata;
360 *p++ = 1;
361 bytes = strlen(sock->proxies[px].username);
362 *p++ = bytes;
363 memcpy(p, sock->proxies[px].username, bytes);
364 p += bytes;
365 bytes = strlen(sock->proxies[px].password);
366 *p++ = bytes;
367 memcpy(p, sock->proxies[px].password, bytes);
368 p += bytes;
369 bytes = p - socksdata;
370 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
371 if(ret) goto proxyfailure;
372 ret = rocksock_recv(sock, socksdata, 2, 2, &bytes);
373 if(ret) goto proxyfailure;
374 if(bytes < 2) goto err_unexpected;
375 else if(socksdata[1] != 0) goto err_proxyauth;
376 } else {
377 goto err_proxyauth;
380 p = socksdata;
381 *p++ = 5;
382 *p++ = 1;
383 *p++ = 0;
384 if(isnumericipv4(targetproxy->hostinfo.host)) {
385 *p++ = 1; // ipv4 method
386 bytes = 4;
387 ipv4fromstring(targetproxy->hostinfo.host, (unsigned char*) p);
388 } else {
389 *p++ = 3; //hostname method, requires the server to do dns lookups.
390 bytes = strlen(targetproxy->hostinfo.host);
391 if(bytes > 255)
392 return MKOERR(sock, RS_E_SOCKS5_AUTH_EXCEEDSIZE);
393 *p++ = bytes;
394 memcpy(p, targetproxy->hostinfo.host, bytes);
396 p+=bytes;
397 *p++ = targetproxy->hostinfo.port / 256;
398 *p++ = targetproxy->hostinfo.port % 256;
399 bytes = p - socksdata;
400 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
401 if(ret) goto proxyfailure;
402 ret = rocksock_recv(sock, socksdata, sizeof(socksdata), sizeof(socksdata), &bytes);
403 if(ret) goto proxyfailure;
404 if(bytes < 2) goto err_unexpected;
405 switch(socksdata[1]) {
406 case 0:
407 break;
408 case 1:
409 ret = MKOERR(sock, RS_E_PROXY_GENERAL_FAILURE);
410 goto proxyfailure;
411 case 2:
412 goto err_proxyauth;
413 case 3:
414 ret = MKOERR(sock, RS_E_TARGETPROXY_NET_UNREACHABLE);
415 goto proxyfailure;
416 case 4:
417 ret = MKOERR(sock, RS_E_TARGETPROXY_HOST_UNREACHABLE);
418 goto proxyfailure;
419 case 5:
420 ret = MKOERR(sock, RS_E_TARGETPROXY_CONN_REFUSED);
421 goto proxyfailure;
422 case 6:
423 ret = MKOERR(sock, RS_E_TARGETPROXY_TTL_EXPIRED);
424 goto proxyfailure;
425 case 7:
426 ret = MKOERR(sock, RS_E_PROXY_COMMAND_NOT_SUPPORTED);
427 goto proxyfailure;
428 case 8:
429 ret = MKOERR(sock, RS_E_PROXY_ADDRESSTYPE_NOT_SUPPORTED);
430 goto proxyfailure;
431 default:
432 goto err_unexpected;
434 break;
435 case RS_PT_HTTP:
436 bytes = snprintf(socksdata, sizeof(socksdata), "CONNECT %s:%d HTTP/1.1\r\n\r\n", targetproxy->hostinfo.host, targetproxy->hostinfo.port);
437 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
438 if(ret) goto proxyfailure;
439 ret = rocksock_recv(sock, socksdata, sizeof(socksdata), sizeof(socksdata), &bytes);
440 if(ret) goto proxyfailure;
441 if(bytes < 12) goto err_unexpected;
442 if(socksdata[9] != '2') goto err_proxyconnect;
443 break;
444 default:
445 break;
449 #ifdef USE_SSL
450 if(useSSL) {
451 ret = rocksock_ssl_connect_fd(sock);
452 if(ret) return ret;
454 #endif
455 return NOERR(sock);
458 typedef enum {
459 RS_OT_SEND = 0,
460 RS_OT_READ
461 } rs_operationType;
463 static int rocksock_operation(rocksock* sock, rs_operationType operation, char* buffer, size_t bufsize, size_t chunksize, size_t* bytes) {
464 if (!sock) return RS_E_NULL;
465 if (!buffer || !bytes || (!bufsize && operation == RS_OT_READ)) return MKOERR(sock, RS_E_NULL);
466 *bytes = 0;
467 struct timeval tv;
468 fd_set fd;
469 fd_set* rfd = NULL;
470 fd_set* wfd = NULL;
471 int ret = 0;
472 size_t bytesleft = bufsize ? bufsize : strlen(buffer);
473 size_t byteswanted;
474 char* bufptr = buffer;
476 if (sock->socket == -1) return MKOERR(sock, RS_E_NO_SOCKET);
477 if(operation == RS_OT_SEND) wfd = &fd;
478 else rfd = &fd;
480 if(sock->timeout) {
481 if(operation == RS_OT_SEND)
482 ret = setsockopt(sock->socket, SOL_SOCKET, SO_SNDTIMEO, (void*) make_timeval(&tv, sock->timeout), sizeof(tv));
483 else
484 ret = setsockopt(sock->socket, SOL_SOCKET, SO_RCVTIMEO, (void*) make_timeval(&tv, sock->timeout), sizeof(tv));
487 if (ret == -1) return MKSYSERR(sock, errno);
489 while(bytesleft) {
490 byteswanted = (chunksize && chunksize < bytesleft) ? chunksize : bytesleft;
491 #ifdef USE_SSL
492 if (sock->ssl) {
493 if(operation == RS_OT_SEND)
494 ret = rocksock_ssl_send(sock, bufptr, byteswanted);
495 else
496 ret = rocksock_ssl_recv(sock, bufptr, byteswanted);
497 } else {
498 #endif
499 /* enforce the timeout by using select() before doing the actual recv/send */
500 FD_SET(sock->socket, &fd);
501 ret=select(sock->socket+1, rfd, wfd, NULL, sock->timeout ? make_timeval(&tv, sock->timeout) : NULL);
502 if(!FD_ISSET(sock->socket, &fd)) MKOERR(sock, RS_E_NULL); // temp test
503 if(ret == -1) {
504 //printf("h: %s, skt: %d, to: %d:%d\n", targethost.host, sock->socket, tv.tv_sec, tv.tv_usec);
505 return MKSYSERR(sock, errno);
507 else if(!ret) return MKOERR(sock, RS_OT_READ ? RS_E_HIT_READTIMEOUT : RS_E_HIT_WRITETIMEOUT);
509 if(operation == RS_OT_SEND)
510 ret = send(sock->socket, bufptr, byteswanted, MSG_NOSIGNAL);
511 else
512 ret = recv(sock->socket, bufptr, byteswanted, 0);
514 #ifdef USE_SSL
516 #endif
518 if(!ret) // The return value will be 0 when the peer has performed an orderly shutdown.
519 return MKOERR(sock, RS_E_REMOTE_DISCONNECTED);
520 else if(ret == -1) {
521 ret = errno;
522 if(ret == EWOULDBLOCK || ret == EINPROGRESS) return MKOERR(sock, RS_OT_READ ? RS_E_HIT_READTIMEOUT : RS_E_HIT_WRITETIMEOUT);
523 return MKSYSERR(sock, errno);
526 bytesleft -= ret;
527 bufptr += ret;
528 *bytes += ret;
529 if(operation == RS_OT_READ && (size_t) ret < byteswanted) break;
531 return NOERR(sock);
534 int rocksock_send(rocksock* sock, char* buffer, size_t bufsize, size_t chunksize, size_t* byteswritten) {
535 return rocksock_operation(sock, RS_OT_SEND, buffer, bufsize, chunksize, byteswritten);
538 int rocksock_recv(rocksock* sock, char* buffer, size_t bufsize, size_t chunksize, size_t* bytesread) {
539 return rocksock_operation(sock, RS_OT_READ, buffer, bufsize, chunksize, bytesread);
542 int rocksock_disconnect(rocksock* sock) {
543 if (!sock) return RS_E_NULL;
544 #ifdef USE_SSL
545 rocksock_ssl_free_context(sock);
546 #endif
547 if(sock->socket != -1) close(sock->socket);
548 sock->socket = -1;
549 return NOERR(sock);
552 int rocksock_clear(rocksock* sock) {
553 if (!sock) return RS_E_NULL;
554 sock->lastproxy = -1;
555 sock->proxies = 0;
556 return NOERR(sock);