update README
[rofl0r-rocksock.git] / rocksock.c
bloba3621d906c2652617ec8b7dc25ddefc16b832a01
1 /*
2 * author: rofl0r (C) 2011-2013
3 * License: LGPL 2.1+ with static linking exception
4 */
6 /*
7 * recognized defines: USE_SSL, ROCKSOCK_FILENAME, NO_DNS_SUPPORT, NO_STRDUP
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 || !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) {
148 if (!sock) return RS_E_NULL;
149 memset(sock, 0, sizeof(rocksock));
150 sock->lastproxy = -1;
151 sock->timeout = 60*1000;
152 return rocksock_seterror(sock, RS_ET_OWN, 0, NULL, 0);
155 static struct timeval* make_timeval(struct timeval* tv, unsigned long timeout) {
156 if(!tv) return NULL;
157 tv->tv_sec = timeout / 1000;
158 tv->tv_usec = 1000 * (timeout % 1000);
159 return tv;
162 static int do_connect(rocksock* sock, rs_resolveStorage* hostinfo, unsigned long timeout) {
163 int flags, ret;
164 fd_set wset;
165 struct timeval tv;
166 int optval;
167 socklen_t optlen = sizeof(optval);
169 sock->socket = socket(hostinfo->hostaddr->ai_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
170 if(sock->socket == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
172 /* the socket has to be made non-blocking temporarily so we can enforce a connect timeout */
173 flags = fcntl(sock->socket, F_GETFL);
174 if(flags == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
176 if(fcntl(sock->socket, F_SETFL, flags | O_NONBLOCK) == -1) return errno;
178 ret = connect(sock->socket, hostinfo->hostaddr->ai_addr, hostinfo->hostaddr->ai_addrlen);
179 if(ret == -1) {
180 ret = errno;
181 if (!(ret == EINPROGRESS || ret == EWOULDBLOCK)) return rocksock_seterror(sock, RS_ET_SYS, ret, ROCKSOCK_FILENAME, __LINE__);
184 if(fcntl(sock->socket, F_SETFL, flags) == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
186 FD_ZERO(&wset);
187 FD_SET(sock->socket, &wset);
189 ret = select(sock->socket+1, NULL, &wset, NULL, timeout ? make_timeval(&tv, timeout) : NULL);
191 if(ret == 1 && FD_ISSET(sock->socket, &wset)) {
192 ret = getsockopt(sock->socket, SOL_SOCKET, SO_ERROR, &optval,&optlen);
193 if(ret == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
194 else if(optval) return rocksock_seterror(sock, RS_ET_SYS, optval, ROCKSOCK_FILENAME, __LINE__);
195 return 0;
196 } else if(ret == 0) return rocksock_seterror(sock, RS_ET_OWN, RS_E_HIT_CONNECTTIMEOUT, ROCKSOCK_FILENAME, __LINE__);
198 return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
201 static int rocksock_setup_socks4_header(rocksock* sock, int is4a, char* buffer, size_t bufsize, rs_proxy* proxy, size_t* bytesused) {
202 int ret;
203 buffer[0] = 4;
204 buffer[1] = 1;
205 buffer[2] = proxy->hostinfo.port / 256;
206 buffer[3] = proxy->hostinfo.port % 256;
208 if(is4a) {
209 buffer[4] = 0;
210 buffer[5] = 0;
211 buffer[6] = 0;
212 buffer[7] = 1;
213 } else {
214 rs_resolveStorage stor;
215 ret = rocksock_resolve_host(sock, &proxy->hostinfo, &stor);
216 if(ret) return ret;
217 if(stor.hostaddr->ai_family != AF_INET)
218 return rocksock_seterror(sock, RS_ET_OWN, RS_E_SOCKS4_NO_IP6, ROCKSOCK_FILENAME, __LINE__);
219 buffer[4] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[0];
220 buffer[5] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[1];
221 buffer[6] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[2];
222 buffer[7] = ((char*) &(((struct sockaddr_in*) stor.hostaddr->ai_addr)->sin_addr.s_addr))[3];
224 buffer[8] = 0;
225 *bytesused = 9;
226 if(is4a) *bytesused += strlen(strncpy(buffer + *bytesused, proxy->hostinfo.host, bufsize - *bytesused))+1;
228 return rocksock_seterror(sock, RS_ET_OWN, 0, NULL, 0);
231 int rocksock_connect(rocksock* sock, char* host, unsigned short port, int useSSL) {
232 ptrdiff_t px;
233 int ret, trysocksv4a;
234 rs_hostInfo* connector;
235 rs_proxy dummy;
236 rs_proxy* targetproxy;
237 char socksdata[768];
238 char* p;
239 size_t socksused = 0, bytes;
240 if (!sock) return RS_E_NULL;
241 if (!host || !port)
242 return rocksock_seterror(sock, RS_ET_OWN, RS_E_NULL, ROCKSOCK_FILENAME, __LINE__);
243 #ifndef USE_SSL
244 if (useSSL) return rocksock_seterror(sock, RS_ET_OWN, RS_E_NO_SSL, ROCKSOCK_FILENAME, __LINE__);
245 #endif
246 #ifdef NO_STRDUP
247 sock->hostinfo.host = host;
248 #else
249 sock->hostinfo.host = strdup(host);
250 #endif
251 sock->hostinfo.port = port;
253 if(sock->lastproxy >= 0)
254 connector = &sock->proxies[0].hostinfo;
255 else
256 connector = &sock->hostinfo;
258 rs_resolveStorage stor;
260 ret = rocksock_resolve_host(sock, connector, &stor);
261 if(ret) {
262 check_proxy0_failure:
263 if(sock->lastproxy >= 0) sock->lasterror.failedProxy = 0;
264 return ret;
267 ret = do_connect(sock, &stor, sock->timeout);
268 if(ret) goto check_proxy0_failure;
270 if(sock->lastproxy >= 0) {
271 dummy.hostinfo = sock->hostinfo;
272 dummy.password = NULL;
273 dummy.username = NULL;
274 dummy.proxytype = RS_PT_NONE;
275 for(px = 0; px <= sock->lastproxy; px++) {
276 if(px == sock->lastproxy)
277 targetproxy = &dummy;
278 else
279 targetproxy = &sock->proxies[px + 1];
280 // send socks connection data
281 switch(sock->proxies[px].proxytype) {
282 case RS_PT_SOCKS4:
283 trysocksv4a = 1;
284 trysocks4:
285 ret = rocksock_setup_socks4_header(sock, trysocksv4a, socksdata, sizeof(socksdata), targetproxy, &socksused);
286 if(ret) {
287 proxyfailure:
288 sock->lasterror.failedProxy = px;
289 return ret;
291 ret = rocksock_send(sock, socksdata, socksused, 0, &bytes);
292 if(ret) goto proxyfailure;
293 ret = rocksock_recv(sock, socksdata, 8, 8, &bytes);
294 if(ret) goto proxyfailure;
295 if(bytes < 8 || socksdata[0] != 0) {
296 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
297 goto proxyfailure;
299 switch(socksdata[1]) {
300 case 0x5a:
301 break;
302 case 0x5b:
303 if(trysocksv4a) {
304 trysocksv4a = 0;
305 goto trysocks4;
307 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_CONNECT_FAILED, ROCKSOCK_FILENAME, __LINE__);
308 goto proxyfailure;
309 case 0x5c: case 0x5d:
310 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
311 goto proxyfailure;
312 default:
313 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
314 goto proxyfailure;
316 break;
317 case RS_PT_SOCKS5:
318 p = socksdata;
319 *p++ = 5;
320 if(sock->proxies[px].username && sock->proxies[px].password) {
321 *p++ = 2;
322 *p++ = 0;
323 *p++ = 2;
324 } else {
325 *p++ = 1;
326 *p++ = 0;
328 bytes = p - socksdata;
329 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
330 if(ret) goto proxyfailure;
331 ret = rocksock_recv(sock, socksdata, 2, 2, &bytes);
332 if(ret) goto proxyfailure;
333 if(bytes < 2 || socksdata[0] != 5) {
334 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
335 goto proxyfailure;
337 if(socksdata[1] == '\xff') {
338 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
339 goto proxyfailure;
340 } else if (socksdata[1] == 2) {
341 if( sock->proxies[px].username && sock->proxies[px].password &&
342 *sock->proxies[px].username && *sock->proxies[px].password) {
344 +----+------+----------+------+----------+
345 |VER | ULEN | UNAME | PLEN | PASSWD |
346 +----+------+----------+------+----------+
347 | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
348 +----+------+----------+------+----------+
350 p = socksdata;
351 *p++ = 1;
352 bytes = strlen(sock->proxies[px].username) & 0xFF;
353 *p++ = bytes;
354 memcpy(p, sock->proxies[px].username, bytes);
355 p += bytes;
356 bytes = strlen(sock->proxies[px].password) & 0xFF;
357 *p++ = bytes;
358 memcpy(p, sock->proxies[px].password, bytes);
359 p += bytes;
360 bytes = p - socksdata;
361 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
362 if(ret) goto proxyfailure;
363 ret = rocksock_recv(sock, socksdata, 2, 2, &bytes);
364 if(ret) goto proxyfailure;
365 if(bytes < 2) {
366 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
367 goto proxyfailure;
368 } else if(socksdata[1] != 0) {
369 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
370 goto proxyfailure;
372 } else {
373 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
374 goto proxyfailure;
377 p = socksdata;
378 *p++ = 5;
379 *p++ = 1;
380 *p++ = 0;
381 if(isnumericipv4(targetproxy->hostinfo.host)) {
382 *p++ = 1; // ipv4 method
383 bytes = 4;
384 ipv4fromstring(targetproxy->hostinfo.host, (unsigned char*) p);
385 } else {
386 *p++ = 3; //hostname method, requires the server to do dns lookups.
387 bytes = strlen(targetproxy->hostinfo.host);
388 if(bytes > 255)
389 return rocksock_seterror(sock, RS_ET_OWN, RS_E_SOCKS5_AUTH_EXCEEDSIZE, ROCKSOCK_FILENAME, __LINE__);
390 *p++ = bytes;
391 memcpy(p, targetproxy->hostinfo.host, bytes);
393 p+=bytes;
394 *p++ = targetproxy->hostinfo.port / 256;
395 *p++ = targetproxy->hostinfo.port % 256;
396 bytes = p - socksdata;
397 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
398 if(ret) goto proxyfailure;
399 ret = rocksock_recv(sock, socksdata, sizeof(socksdata), sizeof(socksdata), &bytes);
400 if(ret) goto proxyfailure;
401 if(bytes < 2) {
402 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
403 goto proxyfailure;
405 switch(socksdata[1]) {
406 case 0:
407 break;
408 case 1:
409 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_GENERAL_FAILURE, ROCKSOCK_FILENAME, __LINE__);
410 goto proxyfailure;
411 case 2:
412 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
413 goto proxyfailure;
414 case 3:
415 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_NET_UNREACHABLE, ROCKSOCK_FILENAME, __LINE__);
416 goto proxyfailure;
417 case 4:
418 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_HOST_UNREACHABLE, ROCKSOCK_FILENAME, __LINE__);
419 goto proxyfailure;
420 case 5:
421 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_CONN_REFUSED, ROCKSOCK_FILENAME, __LINE__);
422 goto proxyfailure;
423 case 6:
424 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_TTL_EXPIRED, ROCKSOCK_FILENAME, __LINE__);
425 goto proxyfailure;
426 case 7:
427 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_COMMAND_NOT_SUPPORTED, ROCKSOCK_FILENAME, __LINE__);
428 goto proxyfailure;
429 case 8:
430 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_ADDRESSTYPE_NOT_SUPPORTED, ROCKSOCK_FILENAME, __LINE__);
431 goto proxyfailure;
432 default:
433 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
434 goto proxyfailure;
436 break;
437 case RS_PT_HTTP:
438 bytes = snprintf(socksdata, sizeof(socksdata), "CONNECT %s:%d HTTP/1.1\r\n\r\n", targetproxy->hostinfo.host, targetproxy->hostinfo.port);
439 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
440 if(ret) goto proxyfailure;
441 ret = rocksock_recv(sock, socksdata, sizeof(socksdata), sizeof(socksdata), &bytes);
442 if(ret) goto proxyfailure;
443 if(bytes < 12) {
444 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
445 goto proxyfailure;
447 if(socksdata[9] != '2') {
448 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_CONNECT_FAILED, ROCKSOCK_FILENAME, __LINE__);
449 goto proxyfailure;
451 break;
452 default:
453 break;
457 #ifdef USE_SSL
458 if(useSSL) {
459 ret = rocksock_ssl_connect_fd(sock);
460 if(ret) return ret;
462 #endif
463 return rocksock_seterror(sock, RS_ET_OWN, 0, NULL, 0);
466 typedef enum {
467 RS_OT_SEND = 0,
468 RS_OT_READ
469 } rs_operationType;
471 static int rocksock_operation(rocksock* sock, rs_operationType operation, char* buffer, size_t bufsize, size_t chunksize, size_t* bytes) {
472 if (!sock) return RS_E_NULL;
473 if (!buffer || !bytes || (!bufsize && operation == RS_OT_READ)) return rocksock_seterror(sock, RS_ET_OWN, RS_E_NULL, ROCKSOCK_FILENAME, __LINE__);
474 *bytes = 0;
475 struct timeval tv;
476 fd_set fd;
477 fd_set* rfd = NULL;
478 fd_set* wfd = NULL;
479 int ret = 0;
480 size_t bytesleft = bufsize ? bufsize : strlen(buffer);
481 size_t byteswanted;
482 char* bufptr = buffer;
484 if (!sock->socket) return rocksock_seterror(sock, RS_ET_OWN, RS_E_NO_SOCKET, ROCKSOCK_FILENAME, __LINE__);
485 if(operation == RS_OT_SEND) wfd = &fd;
486 else rfd = &fd;
488 if(sock->timeout) {
489 if(operation == RS_OT_SEND)
490 ret = setsockopt(sock->socket, SOL_SOCKET, SO_SNDTIMEO, (void*) make_timeval(&tv, sock->timeout), sizeof(tv));
491 else
492 ret = setsockopt(sock->socket, SOL_SOCKET, SO_RCVTIMEO, (void*) make_timeval(&tv, sock->timeout), sizeof(tv));
495 if (ret == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
497 while(bytesleft) {
498 byteswanted = (chunksize && chunksize < bytesleft) ? chunksize : bytesleft;
499 #ifdef USE_SSL
500 if (sock->ssl) {
501 if(operation == RS_OT_SEND)
502 ret = rocksock_ssl_send(sock, bufptr, byteswanted);
503 else
504 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)) rocksock_seterror(sock, RS_ET_OWN, RS_E_NULL, ROCKSOCK_FILENAME, __LINE__); // temp test
512 if(ret == -1) {
513 //printf("h: %s, skt: %d, to: %d:%d\n", sock->hostinfo.host, sock->socket, tv.tv_sec, tv.tv_usec);
514 return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
516 else if(!ret) return rocksock_seterror(sock, RS_ET_OWN, RS_OT_READ ? RS_E_HIT_READTIMEOUT : RS_E_HIT_WRITETIMEOUT, ROCKSOCK_FILENAME, __LINE__);
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 rocksock_seterror(sock, RS_ET_OWN, RS_E_REMOTE_DISCONNECTED, ROCKSOCK_FILENAME, __LINE__);
529 else if(ret == -1) {
530 ret = errno;
531 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__);
532 return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
535 bytesleft -= ret;
536 bufptr += ret;
537 *bytes += ret;
538 if(operation == RS_OT_READ && (size_t) ret < byteswanted) break;
540 return rocksock_seterror(sock, RS_ET_OWN, 0, NULL, 0);
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) close(sock->socket);
557 sock->socket = 0;
558 return rocksock_seterror(sock, RS_ET_OWN, 0, NULL, 0);
561 int rocksock_clear(rocksock* sock) {
562 if (!sock) return RS_E_NULL;
563 ptrdiff_t i;
564 if(sock->lastproxy >= 0) {
565 for (i=0;i<=sock->lastproxy;i++) {
566 #ifndef NO_STRDUP
567 if(sock->proxies[i].username)
568 free(sock->proxies[i].username);
569 if(sock->proxies[i].password)
570 free(sock->proxies[i].password);
571 if(sock->proxies[i].hostinfo.host)
572 free(sock->proxies[i].hostinfo.host);
573 #endif
574 sock->proxies[i].username = NULL;
575 sock->proxies[i].password = NULL;
576 sock->proxies[i].hostinfo.host = NULL;
579 #ifndef NO_STRDUP
580 if(sock->hostinfo.host)
581 free(sock->hostinfo.host);
582 #endif
583 sock->hostinfo.host = NULL;
585 return rocksock_seterror(sock, RS_ET_OWN, 0, NULL, 0);