2 * author: rofl0r (C) 2011-2017
3 * License: LGPL 2.1+ with static linking exception
7 * recognized defines: USE_SSL, ROCKSOCK_FILENAME, NO_DNS_SUPPORT
10 #undef _POSIX_C_SOURCE
11 #define _POSIX_C_SOURCE 200809L
21 #include <sys/select.h>
22 #include <netinet/in.h>
25 #warning compiling without SOCK_CLOEXEC support
26 #define SOCK_CLOEXEC 0
30 #include "rocksock_internal.h"
32 //RcB: SKIPUON "USE_LIBULZ"
33 #include <ulz/strlib.h>
34 #include <ulz/stdio-repl.h>
35 //RcB: SKIPUOFF "USE_LIBULZ"
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
;
44 if(*ipstring
== '.' || !*ipstring
) {
45 fourbytesptr
[outbyte
] = 0;
48 switch(ipstring
- start
) {
50 tmp
= (start
[b
++]-'0')*100;
51 if(tmp
> 200) return 0;
52 fourbytesptr
[outbyte
] += tmp
;
54 fourbytesptr
[outbyte
] += (start
[b
++]-'0')*10;
56 fourbytesptr
[outbyte
] += (start
[b
++]-'0');
64 if(*ipstring
< '0' || *ipstring
> '9') return 0;
66 if(!*ipstring
&& outbyte
< 4) return 0;
69 if(ipstring
[-1]) return 0;
73 static int isnumericipv4(const char* ipstring
) {
75 return ipv4fromstring(ipstring
, ip
);
79 #ifndef ROCKSOCK_FILENAME
80 #define ROCKSOCK_FILENAME __FILE__
84 #define MSG_NOSIGNAL 0
88 #include "rocksock_ssl_internal.h"
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;
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
};
115 struct addrinfo
*best
, *save
;
116 ret
= getaddrinfo(hostinfo
->host
, NULL
, &hints
, &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
);
128 ((struct sockaddr_in6
*) result
->hostaddr
->ai_addr
)->sin6_port
= htons(hostinfo
->port
);
132 return rocksock_seterror(sock
, RS_ET_GAI
, ret
, ROCKSOCK_FILENAME
, __LINE__
);
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
);
146 int rocksock_set_timeout(rocksock
* sock
, unsigned long timeout_millisec
) {
147 if (!sock
) return RS_E_NULL
;
148 sock
->timeout
= timeout_millisec
;
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;
158 sock
->proxies
= proxies
;
162 static struct timeval
* make_timeval(struct timeval
* tv
, unsigned long timeout
) {
164 tv
->tv_sec
= timeout
/ 1000;
165 tv
->tv_usec
= 1000 * (timeout
% 1000);
169 static int do_connect(rocksock
* sock
, rs_resolveStorage
* hostinfo
, unsigned long timeout
) {
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
);
188 if (!(ret
== EINPROGRESS
|| ret
== EWOULDBLOCK
)) return MKSYSERR(sock
, ret
);
191 if(fcntl(sock
->socket
, F_SETFL
, flags
) == -1) return MKSYSERR(sock
, errno
);
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
);
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
) {
212 buffer
[2] = proxy
->hostinfo
.port
/ 256;
213 buffer
[3] = proxy
->hostinfo
.port
% 256;
221 rs_resolveStorage stor
;
222 ret
= rocksock_resolve_host(sock
, &proxy
->hostinfo
, &stor
);
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];
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
);
243 int rocksock_connect(rocksock
* sock
, const char* host
, unsigned short port
, int useSSL
) {
245 int ret
, trysocksv4a
;
246 rs_hostInfo targethost
;
247 rs_hostInfo
* connector
;
249 rs_proxy
* targetproxy
;
252 size_t socksused
= 0, bytes
;
253 if (!sock
) return RS_E_NULL
;
255 return MKOERR(sock
, RS_E_NULL
);
256 size_t hl
= strlen(host
);
258 return MKOERR(sock
, RS_E_HOSTNAME_TOO_LONG
);
260 if (useSSL
) return MKOERR(sock
, RS_E_NO_SSL
);
262 memcpy(targethost
.host
, host
, hl
+1);
263 targethost
.port
= port
;
265 if(sock
->lastproxy
>= 0)
266 connector
= &sock
->proxies
[0].hostinfo
;
268 connector
= &targethost
;
270 rs_resolveStorage stor
;
272 ret
= rocksock_resolve_host(sock
, connector
, &stor
);
274 check_proxy0_failure
:
275 if(sock
->lastproxy
>= 0) sock
->lasterror
.failedProxy
= 0;
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
;
290 targetproxy
= &sock
->proxies
[px
+ 1];
292 // send socks connection data
293 switch(sock
->proxies
[px
].proxytype
) {
297 ret
= rocksock_setup_socks4_header(sock
, trysocksv4a
, socksdata
, targetproxy
, &socksused
);
300 sock
->lasterror
.failedProxy
= px
;
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) {
309 ret
= MKOERR(sock
, RS_E_PROXY_UNEXPECTED_RESPONSE
);
312 switch(socksdata
[1]) {
321 ret
= MKOERR(sock
, RS_E_TARGETPROXY_CONNECT_FAILED
);
323 case 0x5c: case 0x5d:
325 ret
= MKOERR(sock
, RS_E_PROXY_AUTH_FAILED
);
334 if(sock
->proxies
[px
].username
[0] && sock
->proxies
[px
].password
[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') {
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 +----+------+----------+------+----------+
361 bytes
= strlen(sock
->proxies
[px
].username
);
363 memcpy(p
, sock
->proxies
[px
].username
, bytes
);
365 bytes
= strlen(sock
->proxies
[px
].password
);
367 memcpy(p
, sock
->proxies
[px
].password
, 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
;
384 if(isnumericipv4(targetproxy
->hostinfo
.host
)) {
385 *p
++ = 1; // ipv4 method
387 ipv4fromstring(targetproxy
->hostinfo
.host
, (unsigned char*) p
);
389 *p
++ = 3; //hostname method, requires the server to do dns lookups.
390 bytes
= strlen(targetproxy
->hostinfo
.host
);
392 return MKOERR(sock
, RS_E_SOCKS5_AUTH_EXCEEDSIZE
);
394 memcpy(p
, targetproxy
->hostinfo
.host
, 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]) {
409 ret
= MKOERR(sock
, RS_E_PROXY_GENERAL_FAILURE
);
414 ret
= MKOERR(sock
, RS_E_TARGETPROXY_NET_UNREACHABLE
);
417 ret
= MKOERR(sock
, RS_E_TARGETPROXY_HOST_UNREACHABLE
);
420 ret
= MKOERR(sock
, RS_E_TARGETPROXY_CONN_REFUSED
);
423 ret
= MKOERR(sock
, RS_E_TARGETPROXY_TTL_EXPIRED
);
426 ret
= MKOERR(sock
, RS_E_PROXY_COMMAND_NOT_SUPPORTED
);
429 ret
= MKOERR(sock
, RS_E_PROXY_ADDRESSTYPE_NOT_SUPPORTED
);
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
;
451 ret
= rocksock_ssl_connect_fd(sock
);
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
);
472 size_t bytesleft
= bufsize
? bufsize
: strlen(buffer
);
474 char* bufptr
= buffer
;
476 if (sock
->socket
== -1) return MKOERR(sock
, RS_E_NO_SOCKET
);
477 if(operation
== RS_OT_SEND
) wfd
= &fd
;
481 if(operation
== RS_OT_SEND
)
482 ret
= setsockopt(sock
->socket
, SOL_SOCKET
, SO_SNDTIMEO
, (void*) make_timeval(&tv
, sock
->timeout
), sizeof(tv
));
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
);
490 byteswanted
= (chunksize
&& chunksize
< bytesleft
) ? chunksize
: bytesleft
;
493 if(operation
== RS_OT_SEND
)
494 ret
= rocksock_ssl_send(sock
, bufptr
, byteswanted
);
496 ret
= rocksock_ssl_recv(sock
, bufptr
, byteswanted
);
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
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
);
512 ret
= recv(sock
->socket
, bufptr
, byteswanted
, 0);
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
);
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
);
529 if(operation
== RS_OT_READ
&& (size_t) ret
< byteswanted
) break;
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
;
545 rocksock_ssl_free_context(sock
);
547 if(sock
->socket
!= -1) close(sock
->socket
);
552 int rocksock_clear(rocksock
* sock
) {
553 if (!sock
) return RS_E_NULL
;
554 sock
->lastproxy
= -1;