add convenience function rocksock_add_proxy_fromstring()
[rofl0r-rocksock.git] / rocksock.c
blob86859cb60aad25b49a55ed0cd9db4b28b782fa20
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;
100 //#define NO_DNS_SUPPORT
101 static int rocksock_resolve_host(rocksock* sock, rs_hostInfo* hostinfo, rs_resolveStorage* result) {
102 if (!sock) return RS_E_NULL;
103 if (!hostinfo || !hostinfo->host[0] || !hostinfo->port) return rocksock_seterror(sock, RS_ET_OWN, RS_E_NULL, ROCKSOCK_FILENAME, __LINE__);;
105 result->hostaddr = &(result->hostaddr_buf);
107 #ifndef NO_DNS_SUPPORT
108 struct addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_ADDRCONFIG};
109 int ret;
110 struct addrinfo *best, *save;
111 ret = getaddrinfo(hostinfo->host, NULL, &hints, &save);
112 if(!ret) {
113 best = save;
114 while(best->ai_addr->sa_family == AF_INET6 && best->ai_next) best = best->ai_next;
115 *result->hostaddr = *best;
116 result->hostaddr->ai_addr = (struct sockaddr*) &(result->hostaddr_aiaddr_buf);
117 result->hostaddr->ai_next = 0;
118 *result->hostaddr->ai_addr = *best->ai_addr;
120 if(result->hostaddr->ai_addr->sa_family == AF_INET)
121 ((struct sockaddr_in*) result->hostaddr->ai_addr)->sin_port = htons(hostinfo->port);
122 else
123 ((struct sockaddr_in6*) result->hostaddr->ai_addr)->sin6_port = htons(hostinfo->port);
124 freeaddrinfo(save);
125 return 0;
126 } else
127 return rocksock_seterror(sock, RS_ET_GAI, ret, ROCKSOCK_FILENAME, __LINE__);
128 #else
129 result->hostaddr->ai_addr = (struct sockaddr*) &(result->hostaddr_aiaddr_buf);
131 ((struct sockaddr_in*) result->hostaddr->ai_addr)->sin_port = htons(hostinfo->port);
132 ((struct sockaddr_in*) result->hostaddr->ai_addr)->sin_family = AF_INET;
133 result->hostaddr->ai_addr->sa_family = AF_INET;
134 result->hostaddr->ai_addrlen = sizeof(struct sockaddr_in);
135 ipv4fromstring(hostinfo->host, (unsigned char*) &((struct sockaddr_in*) result->hostaddr->ai_addr)->sin_addr);
137 return 0;
138 #endif
141 int rocksock_set_timeout(rocksock* sock, unsigned long timeout_millisec) {
142 if (!sock) return RS_E_NULL;
143 sock->timeout = timeout_millisec;
144 return rocksock_seterror(sock, RS_ET_OWN, 0, NULL, 0);
147 int rocksock_init(rocksock* sock, rs_proxy *proxies) {
148 if (!sock) return RS_E_NULL;
149 memset(sock, 0, sizeof(rocksock));
150 sock->lastproxy = -1;
151 sock->timeout = 60*1000;
152 sock->socket = -1;
153 sock->proxies = proxies;
154 return rocksock_seterror(sock, RS_ET_OWN, 0, NULL, 0);
157 static struct timeval* make_timeval(struct timeval* tv, unsigned long timeout) {
158 if(!tv) return NULL;
159 tv->tv_sec = timeout / 1000;
160 tv->tv_usec = 1000 * (timeout % 1000);
161 return tv;
164 static int do_connect(rocksock* sock, rs_resolveStorage* hostinfo, unsigned long timeout) {
165 int flags, ret;
166 fd_set wset;
167 struct timeval tv;
168 int optval;
169 socklen_t optlen = sizeof(optval);
171 sock->socket = socket(hostinfo->hostaddr->ai_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
172 if(sock->socket == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
174 /* the socket has to be made non-blocking temporarily so we can enforce a connect timeout */
175 flags = fcntl(sock->socket, F_GETFL);
176 if(flags == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
178 if(fcntl(sock->socket, F_SETFL, flags | O_NONBLOCK) == -1) return errno;
180 ret = connect(sock->socket, hostinfo->hostaddr->ai_addr, hostinfo->hostaddr->ai_addrlen);
181 if(ret == -1) {
182 ret = errno;
183 if (!(ret == EINPROGRESS || ret == EWOULDBLOCK)) return rocksock_seterror(sock, RS_ET_SYS, ret, ROCKSOCK_FILENAME, __LINE__);
186 if(fcntl(sock->socket, F_SETFL, flags) == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
188 FD_ZERO(&wset);
189 FD_SET(sock->socket, &wset);
191 ret = select(sock->socket+1, NULL, &wset, NULL, timeout ? make_timeval(&tv, timeout) : NULL);
193 if(ret == 1 && FD_ISSET(sock->socket, &wset)) {
194 ret = getsockopt(sock->socket, SOL_SOCKET, SO_ERROR, &optval,&optlen);
195 if(ret == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
196 else if(optval) return rocksock_seterror(sock, RS_ET_SYS, optval, ROCKSOCK_FILENAME, __LINE__);
197 return 0;
198 } else if(ret == 0) return rocksock_seterror(sock, RS_ET_OWN, RS_E_HIT_CONNECTTIMEOUT, ROCKSOCK_FILENAME, __LINE__);
200 return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
203 static int rocksock_setup_socks4_header(rocksock* sock, int is4a, char* buffer, size_t bufsize, rs_proxy* proxy, size_t* bytesused) {
204 int ret;
205 buffer[0] = 4;
206 buffer[1] = 1;
207 buffer[2] = proxy->hostinfo.port / 256;
208 buffer[3] = proxy->hostinfo.port % 256;
210 if(is4a) {
211 buffer[4] = 0;
212 buffer[5] = 0;
213 buffer[6] = 0;
214 buffer[7] = 1;
215 } else {
216 rs_resolveStorage stor;
217 ret = rocksock_resolve_host(sock, &proxy->hostinfo, &stor);
218 if(ret) return ret;
219 if(stor.hostaddr->ai_family != AF_INET)
220 return rocksock_seterror(sock, RS_ET_OWN, RS_E_SOCKS4_NO_IP6, ROCKSOCK_FILENAME, __LINE__);
221 buffer[4] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[0];
222 buffer[5] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[1];
223 buffer[6] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[2];
224 buffer[7] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[3];
226 buffer[8] = 0;
227 *bytesused = 9;
228 if(is4a) *bytesused += strlen(strncpy(buffer + *bytesused, proxy->hostinfo.host, bufsize - *bytesused))+1;
230 return rocksock_seterror(sock, RS_ET_OWN, 0, NULL, 0);
233 int rocksock_connect(rocksock* sock, const char* host, unsigned short port, int useSSL) {
234 ptrdiff_t px;
235 int ret, trysocksv4a;
236 rs_hostInfo targethost;
237 rs_hostInfo* connector;
238 rs_proxy dummy;
239 rs_proxy* targetproxy;
240 char socksdata[768];
241 char* p;
242 size_t socksused = 0, bytes;
243 if (!sock) return RS_E_NULL;
244 if (!host || !port)
245 return rocksock_seterror(sock, RS_ET_OWN, RS_E_NULL, ROCKSOCK_FILENAME, __LINE__);
246 size_t hl = strlen(host);
247 if(hl > 255)
248 return rocksock_seterror(sock, RS_ET_OWN, RS_E_HOSTNAME_TOO_LONG, ROCKSOCK_FILENAME, __LINE__);
249 #ifndef USE_SSL
250 if (useSSL) return rocksock_seterror(sock, RS_ET_OWN, RS_E_NO_SSL, ROCKSOCK_FILENAME, __LINE__);
251 #endif
252 memcpy(targethost.host, host, hl+1);
253 targethost.port = port;
255 if(sock->lastproxy >= 0)
256 connector = &sock->proxies[0].hostinfo;
257 else
258 connector = &targethost;
260 rs_resolveStorage stor;
262 ret = rocksock_resolve_host(sock, connector, &stor);
263 if(ret) {
264 check_proxy0_failure:
265 if(sock->lastproxy >= 0) sock->lasterror.failedProxy = 0;
266 return ret;
269 ret = do_connect(sock, &stor, sock->timeout);
270 if(ret) goto check_proxy0_failure;
272 if(sock->lastproxy >= 0) {
273 dummy.hostinfo = targethost;
274 dummy.password[0] = 0;
275 dummy.username[0] = 0;
276 dummy.proxytype = RS_PT_NONE;
277 for(px = 0; px <= sock->lastproxy; px++) {
278 if(px == sock->lastproxy)
279 targetproxy = &dummy;
280 else
281 targetproxy = &sock->proxies[px + 1];
282 // send socks connection data
283 switch(sock->proxies[px].proxytype) {
284 case RS_PT_SOCKS4:
285 trysocksv4a = 1;
286 trysocks4:
287 ret = rocksock_setup_socks4_header(sock, trysocksv4a, socksdata, sizeof(socksdata), targetproxy, &socksused);
288 if(ret) {
289 proxyfailure:
290 sock->lasterror.failedProxy = px;
291 return ret;
293 ret = rocksock_send(sock, socksdata, socksused, 0, &bytes);
294 if(ret) goto proxyfailure;
295 ret = rocksock_recv(sock, socksdata, 8, 8, &bytes);
296 if(ret) goto proxyfailure;
297 if(bytes < 8 || socksdata[0] != 0) {
298 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
299 goto proxyfailure;
301 switch(socksdata[1]) {
302 case 0x5a:
303 break;
304 case 0x5b:
305 if(trysocksv4a) {
306 trysocksv4a = 0;
307 goto trysocks4;
309 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_CONNECT_FAILED, ROCKSOCK_FILENAME, __LINE__);
310 goto proxyfailure;
311 case 0x5c: case 0x5d:
312 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
313 goto proxyfailure;
314 default:
315 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
316 goto proxyfailure;
318 break;
319 case RS_PT_SOCKS5:
320 p = socksdata;
321 *p++ = 5;
322 if(sock->proxies[px].username[0] && sock->proxies[px].password[0]) {
323 *p++ = 2;
324 *p++ = 0;
325 *p++ = 2;
326 } else {
327 *p++ = 1;
328 *p++ = 0;
330 bytes = p - socksdata;
331 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
332 if(ret) goto proxyfailure;
333 ret = rocksock_recv(sock, socksdata, 2, 2, &bytes);
334 if(ret) goto proxyfailure;
335 if(bytes < 2 || socksdata[0] != 5) {
336 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
337 goto proxyfailure;
339 if(socksdata[1] == '\xff') {
340 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
341 goto proxyfailure;
342 } else if (socksdata[1] == 2) {
343 if(sock->proxies[px].username[0] && sock->proxies[px].password[0]) {
345 +----+------+----------+------+----------+
346 |VER | ULEN | UNAME | PLEN | PASSWD |
347 +----+------+----------+------+----------+
348 | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
349 +----+------+----------+------+----------+
351 p = socksdata;
352 *p++ = 1;
353 bytes = strlen(sock->proxies[px].username);
354 *p++ = bytes;
355 memcpy(p, sock->proxies[px].username, bytes);
356 p += bytes;
357 bytes = strlen(sock->proxies[px].password);
358 *p++ = bytes;
359 memcpy(p, sock->proxies[px].password, bytes);
360 p += bytes;
361 bytes = p - socksdata;
362 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
363 if(ret) goto proxyfailure;
364 ret = rocksock_recv(sock, socksdata, 2, 2, &bytes);
365 if(ret) goto proxyfailure;
366 if(bytes < 2) {
367 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
368 goto proxyfailure;
369 } else if(socksdata[1] != 0) {
370 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
371 goto proxyfailure;
373 } else {
374 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
375 goto proxyfailure;
378 p = socksdata;
379 *p++ = 5;
380 *p++ = 1;
381 *p++ = 0;
382 if(isnumericipv4(targetproxy->hostinfo.host)) {
383 *p++ = 1; // ipv4 method
384 bytes = 4;
385 ipv4fromstring(targetproxy->hostinfo.host, (unsigned char*) p);
386 } else {
387 *p++ = 3; //hostname method, requires the server to do dns lookups.
388 bytes = strlen(targetproxy->hostinfo.host);
389 if(bytes > 255)
390 return rocksock_seterror(sock, RS_ET_OWN, RS_E_SOCKS5_AUTH_EXCEEDSIZE, ROCKSOCK_FILENAME, __LINE__);
391 *p++ = bytes;
392 memcpy(p, targetproxy->hostinfo.host, bytes);
394 p+=bytes;
395 *p++ = targetproxy->hostinfo.port / 256;
396 *p++ = targetproxy->hostinfo.port % 256;
397 bytes = p - socksdata;
398 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
399 if(ret) goto proxyfailure;
400 ret = rocksock_recv(sock, socksdata, sizeof(socksdata), sizeof(socksdata), &bytes);
401 if(ret) goto proxyfailure;
402 if(bytes < 2) {
403 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
404 goto proxyfailure;
406 switch(socksdata[1]) {
407 case 0:
408 break;
409 case 1:
410 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_GENERAL_FAILURE, ROCKSOCK_FILENAME, __LINE__);
411 goto proxyfailure;
412 case 2:
413 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
414 goto proxyfailure;
415 case 3:
416 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_NET_UNREACHABLE, ROCKSOCK_FILENAME, __LINE__);
417 goto proxyfailure;
418 case 4:
419 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_HOST_UNREACHABLE, ROCKSOCK_FILENAME, __LINE__);
420 goto proxyfailure;
421 case 5:
422 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_CONN_REFUSED, ROCKSOCK_FILENAME, __LINE__);
423 goto proxyfailure;
424 case 6:
425 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_TTL_EXPIRED, ROCKSOCK_FILENAME, __LINE__);
426 goto proxyfailure;
427 case 7:
428 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_COMMAND_NOT_SUPPORTED, ROCKSOCK_FILENAME, __LINE__);
429 goto proxyfailure;
430 case 8:
431 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_ADDRESSTYPE_NOT_SUPPORTED, ROCKSOCK_FILENAME, __LINE__);
432 goto proxyfailure;
433 default:
434 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
435 goto proxyfailure;
437 break;
438 case RS_PT_HTTP:
439 bytes = snprintf(socksdata, sizeof(socksdata), "CONNECT %s:%d HTTP/1.1\r\n\r\n", targetproxy->hostinfo.host, targetproxy->hostinfo.port);
440 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
441 if(ret) goto proxyfailure;
442 ret = rocksock_recv(sock, socksdata, sizeof(socksdata), sizeof(socksdata), &bytes);
443 if(ret) goto proxyfailure;
444 if(bytes < 12) {
445 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
446 goto proxyfailure;
448 if(socksdata[9] != '2') {
449 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_CONNECT_FAILED, ROCKSOCK_FILENAME, __LINE__);
450 goto proxyfailure;
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 rocksock_seterror(sock, RS_ET_OWN, 0, NULL, 0);
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 rocksock_seterror(sock, RS_ET_OWN, RS_E_NULL, ROCKSOCK_FILENAME, __LINE__);
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 rocksock_seterror(sock, RS_ET_OWN, RS_E_NO_SOCKET, ROCKSOCK_FILENAME, __LINE__);
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 rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
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);
507 } else {
508 #endif
509 /* enforce the timeout by using select() before doing the actual recv/send */
510 FD_SET(sock->socket, &fd);
511 ret=select(sock->socket+1, rfd, wfd, NULL, sock->timeout ? make_timeval(&tv, sock->timeout) : NULL);
512 if(!FD_ISSET(sock->socket, &fd)) rocksock_seterror(sock, RS_ET_OWN, RS_E_NULL, ROCKSOCK_FILENAME, __LINE__); // temp test
513 if(ret == -1) {
514 //printf("h: %s, skt: %d, to: %d:%d\n", targethost.host, sock->socket, tv.tv_sec, tv.tv_usec);
515 return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
517 else if(!ret) return rocksock_seterror(sock, RS_ET_OWN, RS_OT_READ ? RS_E_HIT_READTIMEOUT : RS_E_HIT_WRITETIMEOUT, ROCKSOCK_FILENAME, __LINE__);
519 if(operation == RS_OT_SEND)
520 ret = send(sock->socket, bufptr, byteswanted, MSG_NOSIGNAL);
521 else
522 ret = recv(sock->socket, bufptr, byteswanted, 0);
524 #ifdef USE_SSL
526 #endif
528 if(!ret) // The return value will be 0 when the peer has performed an orderly shutdown.
529 return rocksock_seterror(sock, RS_ET_OWN, RS_E_REMOTE_DISCONNECTED, ROCKSOCK_FILENAME, __LINE__);
530 else if(ret == -1) {
531 ret = errno;
532 if(ret == EWOULDBLOCK || ret == EINPROGRESS) return rocksock_seterror(sock, RS_ET_OWN, RS_OT_READ ? RS_E_HIT_READTIMEOUT : RS_E_HIT_WRITETIMEOUT, ROCKSOCK_FILENAME, __LINE__);
533 return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
536 bytesleft -= ret;
537 bufptr += ret;
538 *bytes += ret;
539 if(operation == RS_OT_READ && (size_t) ret < byteswanted) break;
541 return rocksock_seterror(sock, RS_ET_OWN, 0, NULL, 0);
544 int rocksock_send(rocksock* sock, char* buffer, size_t bufsize, size_t chunksize, size_t* byteswritten) {
545 return rocksock_operation(sock, RS_OT_SEND, buffer, bufsize, chunksize, byteswritten);
548 int rocksock_recv(rocksock* sock, char* buffer, size_t bufsize, size_t chunksize, size_t* bytesread) {
549 return rocksock_operation(sock, RS_OT_READ, buffer, bufsize, chunksize, bytesread);
552 int rocksock_disconnect(rocksock* sock) {
553 if (!sock) return RS_E_NULL;
554 #ifdef USE_SSL
555 rocksock_ssl_free_context(sock);
556 #endif
557 if(sock->socket != -1) close(sock->socket);
558 sock->socket = -1;
559 return rocksock_seterror(sock, RS_ET_OWN, 0, NULL, 0);
562 int rocksock_clear(rocksock* sock) {
563 if (!sock) return RS_E_NULL;
564 sock->lastproxy = -1;
565 sock->proxies = 0;
566 return rocksock_seterror(sock, RS_ET_OWN, 0, NULL, 0);