use same address family for socket() and connect()
[rofl0r-rocksock.git] / rocksock.c
blob815106897462cf94b018e13910b150953a9ce85a
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 #include "rocksock.h"
25 #include "rocksock_internal.h"
26 #include "../lib/include/strlib.h"
28 #ifndef ROCKSOCK_FILENAME
29 #define ROCKSOCK_FILENAME __FILE__
30 #endif
32 #ifndef MSG_NOSIGNAL
33 #define MSG_NOSIGNAL 0
34 #endif
36 #ifdef USE_SSL
37 #include "rocksock_ssl_internal.h"
38 #endif
40 int rocksock_seterror(rocksock* sock, rs_errorType errortype, int error, const char* file, int line) {
41 if (!sock) return RS_E_NULL;
42 sock->lasterror.errortype = errortype;
43 sock->lasterror.error = error;
44 sock->lasterror.line = line;
45 sock->lasterror.file = file;
46 sock->lasterror.failedProxy = -1;
47 switch(errortype) {
48 #ifndef NO_DNS_SUPPORT
49 case RS_ET_GAI:
50 sock->lasterror.errormsg = (char*) gai_strerror(error);
51 break;
52 #endif
53 case RS_ET_OWN:
54 if (error < RS_E_MAX_ERROR)
55 sock->lasterror.errormsg = (char*) rs_errorMap[error];
56 else
57 sock->lasterror.errormsg = NULL;
58 break;
59 case RS_ET_SYS:
60 sock->lasterror.errormsg = strerror(error);
61 break;
62 #ifdef USE_SSL
63 case RS_ET_SSL:
64 sock->lasterror.errormsg = (char*) rocksock_ssl_strerror(sock, error);
65 break;
66 #endif
67 default:
68 sock->lasterror.errormsg = NULL;
69 break;
71 return error;
73 //#define NO_DNS_SUPPORT
74 static int rocksock_resolve_host(rocksock* sock, rs_hostInfo* hostinfo) {
75 #ifndef NO_DNS_SUPPORT
76 struct addrinfo hints;
77 int ret;
78 #endif
79 if (!sock) return RS_E_NULL;
80 if (!hostinfo || !hostinfo->host || !hostinfo->port) return rocksock_seterror(sock, RS_ET_OWN, RS_E_NULL, ROCKSOCK_FILENAME, __LINE__);;
81 #ifndef NO_DNS_SUPPORT
82 memset(&hints, 0, sizeof(hints));
83 hints.ai_family = AF_UNSPEC;
84 hints.ai_socktype = SOCK_STREAM;
85 ret = getaddrinfo(hostinfo->host, NULL, &hints, &hostinfo->hostaddr);
86 if(!ret) {
87 if(hostinfo->hostaddr->ai_addr->sa_family == AF_INET)
88 ((struct sockaddr_in*) hostinfo->hostaddr->ai_addr)->sin_port = htons(hostinfo->port);
89 else
90 ((struct sockaddr_in6*) hostinfo->hostaddr->ai_addr)->sin6_port = htons(hostinfo->port);
91 return 0;
92 } else
93 return rocksock_seterror(sock, RS_ET_GAI, ret, ROCKSOCK_FILENAME, __LINE__);
94 #else
95 hostinfo->hostaddr = &(hostinfo->hostaddr_buf);
96 hostinfo->hostaddr->ai_addr = (struct sockaddr*) &(hostinfo->hostaddr_aiaddr_buf);
97 ((struct sockaddr_in*) hostinfo->hostaddr->ai_addr)->sin_port = htons(hostinfo->port);
98 ((struct sockaddr_in*) hostinfo->hostaddr->ai_addr)->sin_family = AF_INET;
99 hostinfo->hostaddr->ai_addr->sa_family = AF_INET;
100 hostinfo->hostaddr->ai_addrlen = sizeof(struct sockaddr_in);
101 ipv4fromstring(hostinfo->host, (unsigned char*) &((struct sockaddr_in*) hostinfo->hostaddr->ai_addr)->sin_addr);
103 return 0;
104 #endif
107 int rocksock_set_timeout(rocksock* sock, unsigned long timeout_millisec) {
108 if (!sock) return RS_E_NULL;
109 sock->timeout = timeout_millisec;
110 return rocksock_seterror(sock, RS_ET_NO_ERROR, 0, NULL, 0);
113 int rocksock_init(rocksock* sock) {
114 if (!sock) return RS_E_NULL;
115 memset(sock, 0, sizeof(rocksock));
116 sock->lastproxy = -1;
117 sock->timeout = 60*1000;
118 return rocksock_seterror(sock, RS_ET_NO_ERROR, 0, NULL, 0);
121 static struct timeval* make_timeval(struct timeval* tv, unsigned long timeout) {
122 if(!tv) return NULL;
123 tv->tv_sec = timeout / 1000;
124 tv->tv_usec = 1000 * (timeout % 1000);
125 return tv;
128 static int do_connect(rocksock* sock, rs_hostInfo* hostinfo, unsigned long timeout) {
129 int flags, ret;
130 fd_set wset;
131 struct timeval tv;
132 int optval;
133 socklen_t optlen = sizeof(optval);
135 sock->socket = socket(hostinfo->hostaddr->ai_family, SOCK_STREAM, 0);
136 if(sock->socket == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
138 flags = fcntl(sock->socket, F_GETFL);
139 if(flags == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
141 if(fcntl(sock->socket, F_SETFL, flags | O_NONBLOCK) == -1) return errno;
143 ret = connect(sock->socket, hostinfo->hostaddr->ai_addr, hostinfo->hostaddr->ai_addrlen);
144 if(ret == -1) {
145 ret = errno;
146 if (!(ret == EINPROGRESS || ret == EWOULDBLOCK)) return rocksock_seterror(sock, RS_ET_SYS, ret, ROCKSOCK_FILENAME, __LINE__);
149 if(fcntl(sock->socket, F_SETFL, flags) == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
151 FD_ZERO(&wset);
152 FD_SET(sock->socket, &wset);
154 ret = select(sock->socket+1, NULL, &wset, NULL, timeout ? make_timeval(&tv, timeout) : NULL);
156 if(ret == 1 && FD_ISSET(sock->socket, &wset)) {
157 ret = getsockopt(sock->socket, SOL_SOCKET, SO_ERROR, &optval,&optlen);
158 if(ret == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
159 else if(optval) return rocksock_seterror(sock, RS_ET_SYS, optval, ROCKSOCK_FILENAME, __LINE__);
160 return 0;
161 } else if(ret == 0) return rocksock_seterror(sock, RS_ET_OWN, RS_E_HIT_CONNECTTIMEOUT, ROCKSOCK_FILENAME, __LINE__);
163 return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
166 static int rocksock_setup_socks4_header(rocksock* sock, int is4a, char* buffer, size_t bufsize, rs_proxy* proxy, size_t* bytesused) {
167 int ret;
168 buffer[0] = 4;
169 buffer[1] = 1;
170 buffer[2] = proxy->hostinfo.port / 256;
171 buffer[3] = proxy->hostinfo.port % 256;
173 if(is4a) {
174 buffer[4] = 0;
175 buffer[5] = 0;
176 buffer[6] = 0;
177 buffer[7] = 1;
178 } else {
179 ret = rocksock_resolve_host(sock, &proxy->hostinfo);
180 if(ret) return ret;
181 if(proxy->hostinfo.hostaddr->ai_family != AF_INET)
182 return rocksock_seterror(sock, RS_ET_OWN, RS_E_SOCKS4_NO_IP6, ROCKSOCK_FILENAME, __LINE__);
183 buffer[4] = ((char*) &(((struct sockaddr_in*) proxy->hostinfo.hostaddr->ai_addr)->sin_addr.s_addr))[0];
184 buffer[5] = ((char*) &(((struct sockaddr_in*) proxy->hostinfo.hostaddr->ai_addr)->sin_addr.s_addr))[1];
185 buffer[6] = ((char*) &(((struct sockaddr_in*) proxy->hostinfo.hostaddr->ai_addr)->sin_addr.s_addr))[2];
186 buffer[7] = ((char*) &(((struct sockaddr_in*) proxy->hostinfo.hostaddr->ai_addr)->sin_addr.s_addr))[3];
188 buffer[8] = 0;
189 *bytesused = 9;
190 if(is4a) *bytesused += strlen(strncpy(buffer + *bytesused, proxy->hostinfo.host, bufsize - *bytesused))+1;
192 return rocksock_seterror(sock, RS_ET_NO_ERROR, 0, NULL, 0);
195 int rocksock_connect(rocksock* sock, char* host, unsigned short port, int useSSL) {
196 ptrdiff_t i;
197 int ret, trysocksv4a;
198 rs_hostInfo* connector;
199 rs_proxy dummy;
200 rs_proxy* targetproxy;
201 char socksdata[768];
202 char* p;
203 size_t socksused = 0, bytes;
204 if (!sock) return RS_E_NULL;
205 if (!host || !port)
206 return rocksock_seterror(sock, RS_ET_OWN, RS_E_NULL, ROCKSOCK_FILENAME, __LINE__);
207 #ifndef USE_SSL
208 if (useSSL) return rocksock_seterror(sock, RS_ET_OWN, RS_E_NO_SSL, ROCKSOCK_FILENAME, __LINE__);
209 #endif
210 #ifdef NO_STRDUP
211 sock->hostinfo.host = host;
212 #else
213 sock->hostinfo.host = strdup(host);
214 #endif
215 sock->hostinfo.port = port;
217 if(sock->lastproxy >= 0)
218 connector = &sock->proxies[0].hostinfo;
219 else
220 connector = &sock->hostinfo;
222 ret = rocksock_resolve_host(sock, connector);
223 if(ret) {
224 check_proxy0_failure:
225 if(sock->lastproxy >= 0) sock->lasterror.failedProxy = 0;
226 return ret;
229 ret = do_connect(sock, connector, sock->timeout);
230 if(ret) goto check_proxy0_failure;
232 if(sock->lastproxy >= 0) {
233 dummy.hostinfo = sock->hostinfo;
234 dummy.password = NULL;
235 dummy.username = NULL;
236 dummy.proxytype = RS_PT_NONE;
237 for(i=1;i<=sock->lastproxy+1;i++) {
238 if(i > sock->lastproxy)
239 targetproxy = &dummy;
240 else
241 targetproxy = &sock->proxies[i];
242 // send socks connection data
243 switch(sock->proxies[i-1].proxytype) {
244 case RS_PT_SOCKS4:
245 trysocksv4a = 1;
246 trysocks4:
247 ret = rocksock_setup_socks4_header(sock, trysocksv4a, socksdata, sizeof(socksdata), targetproxy, &socksused);
248 if(ret) {
249 proxyfailure:
250 sock->lasterror.failedProxy = i - 1;
251 return ret;
253 ret = rocksock_send(sock, socksdata, socksused, 0, &bytes);
254 if(ret) goto proxyfailure;
255 ret = rocksock_recv(sock, socksdata, 8, 8, &bytes);
256 if(ret) goto proxyfailure;
257 if(bytes < 8 || socksdata[0] != 0) {
258 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
259 goto proxyfailure;
261 switch(socksdata[1]) {
262 case 0x5a:
263 break;
264 case 0x5b:
265 if(trysocksv4a) {
266 trysocksv4a = 0;
267 goto trysocks4;
269 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_CONNECT_FAILED, ROCKSOCK_FILENAME, __LINE__);
270 goto proxyfailure;
271 case 0x5c: case 0x5d:
272 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
273 goto proxyfailure;
274 default:
275 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
276 goto proxyfailure;
278 break;
279 case RS_PT_SOCKS5:
280 p = socksdata;
281 *p++ = 5;
282 if(sock->proxies[i-1].username && sock->proxies[i-1].password) {
283 *p++ = 2;
284 *p++ = 0;
285 *p++ = 2;
286 } else {
287 *p++ = 1;
288 *p++ = 0;
290 bytes = p - socksdata;
291 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
292 if(ret) goto proxyfailure;
293 ret = rocksock_recv(sock, socksdata, 2, 2, &bytes);
294 if(ret) goto proxyfailure;
295 if(bytes < 2 || socksdata[0] != 5) {
296 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
297 goto proxyfailure;
299 if(socksdata[1] == '\xff') {
300 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
301 goto proxyfailure;
302 } else if (socksdata[1] == 2) {
303 if( sock->proxies[i-1].username && sock->proxies[i-1].password &&
304 *sock->proxies[i-1].username && *sock->proxies[i-1].password) {
306 +----+------+----------+------+----------+
307 |VER | ULEN | UNAME | PLEN | PASSWD |
308 +----+------+----------+------+----------+
309 | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
310 +----+------+----------+------+----------+
312 p = socksdata;
313 *p++ = 1;
314 bytes = strlen(sock->proxies[i-1].username) & 0xFF;
315 *p++ = bytes;
316 memcpy(p, sock->proxies[i-1].username, bytes);
317 p += bytes;
318 bytes = strlen(sock->proxies[i-1].password) & 0xFF;
319 *p++ = bytes;
320 memcpy(p, sock->proxies[i-1].password, bytes);
321 p += bytes;
322 bytes = p - socksdata;
323 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
324 if(ret) goto proxyfailure;
325 ret = rocksock_recv(sock, socksdata, 2, 2, &bytes);
326 if(ret) goto proxyfailure;
327 if(bytes < 2) {
328 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
329 goto proxyfailure;
330 } else if(socksdata[1] != 0) {
331 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
332 goto proxyfailure;
334 } else {
335 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
336 goto proxyfailure;
339 p = socksdata;
340 *p++ = 5;
341 *p++ = 1;
342 *p++ = 0;
343 if(isnumericipv4(targetproxy->hostinfo.host)) {
344 *p++ = 1; // ipv4 method
345 bytes = 4;
346 ipv4fromstring(targetproxy->hostinfo.host, (unsigned char*) p);
347 } else {
348 *p++ = 3; //hostname method, requires the server to do dns lookups.
349 bytes = strlen(targetproxy->hostinfo.host);
350 if(bytes > 255)
351 return rocksock_seterror(sock, RS_ET_OWN, RS_E_SOCKS5_AUTH_EXCEEDSIZE, ROCKSOCK_FILENAME, __LINE__);
352 *p++ = bytes;
353 memcpy(p, targetproxy->hostinfo.host, bytes);
355 p+=bytes;
356 *p++ = targetproxy->hostinfo.port / 256;
357 *p++ = targetproxy->hostinfo.port % 256;
358 bytes = p - socksdata;
359 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
360 if(ret) goto proxyfailure;
361 ret = rocksock_recv(sock, socksdata, sizeof(socksdata), sizeof(socksdata), &bytes);
362 if(ret) goto proxyfailure;
363 if(bytes < 2) {
364 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
365 goto proxyfailure;
367 switch(socksdata[1]) {
368 case 0:
369 break;
370 case 1:
371 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_GENERAL_FAILURE, ROCKSOCK_FILENAME, __LINE__);
372 goto proxyfailure;
373 case 2:
374 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_AUTH_FAILED, ROCKSOCK_FILENAME, __LINE__);
375 goto proxyfailure;
376 case 3:
377 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_NET_UNREACHABLE, ROCKSOCK_FILENAME, __LINE__);
378 goto proxyfailure;
379 case 4:
380 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_HOST_UNREACHABLE, ROCKSOCK_FILENAME, __LINE__);
381 goto proxyfailure;
382 case 5:
383 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_CONN_REFUSED, ROCKSOCK_FILENAME, __LINE__);
384 goto proxyfailure;
385 case 6:
386 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_TTL_EXPIRED, ROCKSOCK_FILENAME, __LINE__);
387 goto proxyfailure;
388 case 7:
389 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_COMMAND_NOT_SUPPORTED, ROCKSOCK_FILENAME, __LINE__);
390 goto proxyfailure;
391 case 8:
392 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_ADDRESSTYPE_NOT_SUPPORTED, ROCKSOCK_FILENAME, __LINE__);
393 goto proxyfailure;
394 default:
395 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
396 goto proxyfailure;
398 break;
399 case RS_PT_HTTP:
400 bytes = ulz_snprintf(socksdata, sizeof(socksdata), "CONNECT %s:%d HTTP/1.1\r\n\r\n", targetproxy->hostinfo.host, targetproxy->hostinfo.port);
401 ret = rocksock_send(sock, socksdata, bytes, bytes, &bytes);
402 if(ret) goto proxyfailure;
403 ret = rocksock_recv(sock, socksdata, sizeof(socksdata), sizeof(socksdata), &bytes);
404 if(ret) goto proxyfailure;
405 if(bytes < 12) {
406 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_PROXY_UNEXPECTED_RESPONSE, ROCKSOCK_FILENAME, __LINE__);
407 goto proxyfailure;
409 if(socksdata[9] != '2') {
410 ret = rocksock_seterror(sock, RS_ET_OWN, RS_E_TARGETPROXY_CONNECT_FAILED, ROCKSOCK_FILENAME, __LINE__);
411 goto proxyfailure;
413 break;
414 default:
415 break;
419 #ifdef USE_SSL
420 if(useSSL) {
421 ret = rocksock_ssl_connect_fd(sock);
422 if(ret) return ret;
424 #endif
425 return rocksock_seterror(sock, RS_ET_NO_ERROR, 0, NULL, 0);
428 typedef enum {
429 RS_OT_SEND = 0,
430 RS_OT_READ
431 } rs_operationType;
433 static int rocksock_operation(rocksock* sock, rs_operationType operation, char* buffer, size_t bufsize, size_t chunksize, size_t* bytes) {
434 if (!sock) return RS_E_NULL;
435 if (!buffer || !bytes || (!bufsize && operation == RS_OT_READ)) return rocksock_seterror(sock, RS_ET_OWN, RS_E_NULL, ROCKSOCK_FILENAME, __LINE__);
436 *bytes = 0;
437 struct timeval tv;
438 fd_set fd;
439 fd_set* rfd = NULL;
440 fd_set* wfd = NULL;
441 int ret = 0;
442 size_t bytesleft = bufsize ? bufsize : strlen(buffer);
443 size_t byteswanted;
444 char* bufptr = buffer;
446 if (!sock->socket) return rocksock_seterror(sock, RS_ET_OWN, RS_E_NO_SOCKET, ROCKSOCK_FILENAME, __LINE__);
447 if(operation == RS_OT_SEND) wfd = &fd;
448 else rfd = &fd;
450 if(sock->timeout) {
451 if(operation == RS_OT_SEND)
452 ret = setsockopt(sock->socket, SOL_SOCKET, SO_SNDTIMEO, (void*) make_timeval(&tv, sock->timeout), sizeof(tv));
453 else
454 ret = setsockopt(sock->socket, SOL_SOCKET, SO_RCVTIMEO, (void*) make_timeval(&tv, sock->timeout), sizeof(tv));
457 if (ret == -1) return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
459 while(bytesleft) {
460 FD_SET(sock->socket, &fd);
461 ret=select(sock->socket+1, rfd, wfd, NULL, sock->timeout ? make_timeval(&tv, sock->timeout) : NULL);
462 if(!FD_ISSET(sock->socket, &fd)) rocksock_seterror(sock, RS_ET_OWN, RS_E_NULL, ROCKSOCK_FILENAME, __LINE__); // temp test
463 if(ret == -1) {
464 //printf("h: %s, skt: %d, to: %d:%d\n", sock->hostinfo.host, sock->socket, tv.tv_sec, tv.tv_usec);
465 return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
467 else if(!ret) return rocksock_seterror(sock, RS_ET_OWN, RS_OT_READ ? RS_E_HIT_READTIMEOUT : RS_E_HIT_WRITETIMEOUT, ROCKSOCK_FILENAME, __LINE__);
468 byteswanted = (chunksize && chunksize < bytesleft) ? chunksize : bytesleft;
469 #ifdef USE_SSL
470 if (sock->ssl) {
471 if(operation == RS_OT_SEND)
472 ret = rocksock_ssl_send(sock, bufptr, byteswanted);
473 else
474 ret = rocksock_ssl_recv(sock, bufptr, byteswanted);
476 } else
477 #endif
478 if(operation == RS_OT_SEND)
479 ret = send(sock->socket, bufptr, byteswanted, MSG_NOSIGNAL);
480 else
481 ret = recv(sock->socket, bufptr, byteswanted, 0);
483 if(!ret) // The return value will be 0 when the peer has performed an orderly shutdown.
484 //return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
485 break;
486 else if(ret == -1) {
487 ret = errno;
488 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__);
489 return rocksock_seterror(sock, RS_ET_SYS, errno, ROCKSOCK_FILENAME, __LINE__);
492 bytesleft -= ret;
493 bufptr += ret;
494 *bytes += ret;
495 if(operation == RS_OT_READ && (size_t) ret < byteswanted) break;
497 return rocksock_seterror(sock, RS_ET_NO_ERROR, 0, NULL, 0);
500 int rocksock_send(rocksock* sock, char* buffer, size_t bufsize, size_t chunksize, size_t* byteswritten) {
501 return rocksock_operation(sock, RS_OT_SEND, buffer, bufsize, chunksize, byteswritten);
504 int rocksock_recv(rocksock* sock, char* buffer, size_t bufsize, size_t chunksize, size_t* bytesread) {
505 return rocksock_operation(sock, RS_OT_READ, buffer, bufsize, chunksize, bytesread);
508 int rocksock_disconnect(rocksock* sock) {
509 if (!sock) return RS_E_NULL;
510 #ifdef USE_SSL
511 rocksock_ssl_free_context(sock);
512 #endif
513 if(sock->socket) close(sock->socket);
514 sock->socket = 0;
515 return rocksock_seterror(sock, RS_ET_NO_ERROR, 0, NULL, 0);
518 int rocksock_clear(rocksock* sock) {
519 if (!sock) return RS_E_NULL;
520 ptrdiff_t i;
521 if(sock->lastproxy >= 0) {
522 for (i=0;i<=sock->lastproxy;i++) {
523 #ifndef NO_STRDUP
524 if(sock->proxies[i].username)
525 free(sock->proxies[i].username);
526 if(sock->proxies[i].password)
527 free(sock->proxies[i].password);
528 if(sock->proxies[i].hostinfo.host)
529 free(sock->proxies[i].hostinfo.host);
530 #endif
531 sock->proxies[i].username = NULL;
532 sock->proxies[i].password = NULL;
533 sock->proxies[i].hostinfo.host = NULL;
534 #ifndef NO_DNS_SUPPORT
535 if(sock->proxies[i].hostinfo.hostaddr)
536 freeaddrinfo(sock->proxies[i].hostinfo.hostaddr);
537 #endif
538 sock->proxies[i].hostinfo.hostaddr = NULL;
541 #ifndef NO_STRDUP
542 if(sock->hostinfo.host)
543 free(sock->hostinfo.host);
544 #endif
545 sock->hostinfo.host = NULL;
546 #ifndef NO_DNS_SUPPORT
547 if(sock->hostinfo.hostaddr)
548 freeaddrinfo(sock->hostinfo.hostaddr);
549 #endif
550 sock->hostinfo.hostaddr = NULL;
552 return rocksock_seterror(sock, RS_ET_NO_ERROR, 0, NULL, 0);