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;
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
};
110 struct addrinfo
*best
, *save
;
111 ret
= getaddrinfo(hostinfo
->host
, NULL
, &hints
, &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
);
123 ((struct sockaddr_in6
*) result
->hostaddr
->ai_addr
)->sin6_port
= htons(hostinfo
->port
);
127 return rocksock_seterror(sock
, RS_ET_GAI
, ret
, ROCKSOCK_FILENAME
, __LINE__
);
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
);
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;
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
) {
159 tv
->tv_sec
= timeout
/ 1000;
160 tv
->tv_usec
= 1000 * (timeout
% 1000);
164 static int do_connect(rocksock
* sock
, rs_resolveStorage
* hostinfo
, unsigned long timeout
) {
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
);
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__
);
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__
);
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
) {
207 buffer
[2] = proxy
->hostinfo
.port
/ 256;
208 buffer
[3] = proxy
->hostinfo
.port
% 256;
216 rs_resolveStorage stor
;
217 ret
= rocksock_resolve_host(sock
, &proxy
->hostinfo
, &stor
);
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];
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
) {
235 int ret
, trysocksv4a
;
236 rs_hostInfo targethost
;
237 rs_hostInfo
* connector
;
239 rs_proxy
* targetproxy
;
242 size_t socksused
= 0, bytes
;
243 if (!sock
) return RS_E_NULL
;
245 return rocksock_seterror(sock
, RS_ET_OWN
, RS_E_NULL
, ROCKSOCK_FILENAME
, __LINE__
);
246 size_t hl
= strlen(host
);
248 return rocksock_seterror(sock
, RS_ET_OWN
, RS_E_HOSTNAME_TOO_LONG
, ROCKSOCK_FILENAME
, __LINE__
);
250 if (useSSL
) return rocksock_seterror(sock
, RS_ET_OWN
, RS_E_NO_SSL
, ROCKSOCK_FILENAME
, __LINE__
);
252 memcpy(targethost
.host
, host
, hl
+1);
253 targethost
.port
= port
;
255 if(sock
->lastproxy
>= 0)
256 connector
= &sock
->proxies
[0].hostinfo
;
258 connector
= &targethost
;
260 rs_resolveStorage stor
;
262 ret
= rocksock_resolve_host(sock
, connector
, &stor
);
264 check_proxy0_failure
:
265 if(sock
->lastproxy
>= 0) sock
->lasterror
.failedProxy
= 0;
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
;
281 targetproxy
= &sock
->proxies
[px
+ 1];
282 // send socks connection data
283 switch(sock
->proxies
[px
].proxytype
) {
287 ret
= rocksock_setup_socks4_header(sock
, trysocksv4a
, socksdata
, sizeof(socksdata
), targetproxy
, &socksused
);
290 sock
->lasterror
.failedProxy
= px
;
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__
);
301 switch(socksdata
[1]) {
309 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_TARGETPROXY_CONNECT_FAILED
, ROCKSOCK_FILENAME
, __LINE__
);
311 case 0x5c: case 0x5d:
312 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_PROXY_AUTH_FAILED
, ROCKSOCK_FILENAME
, __LINE__
);
315 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_PROXY_UNEXPECTED_RESPONSE
, ROCKSOCK_FILENAME
, __LINE__
);
322 if(sock
->proxies
[px
].username
[0] && sock
->proxies
[px
].password
[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__
);
339 if(socksdata
[1] == '\xff') {
340 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_PROXY_AUTH_FAILED
, ROCKSOCK_FILENAME
, __LINE__
);
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 +----+------+----------+------+----------+
353 bytes
= strlen(sock
->proxies
[px
].username
);
355 memcpy(p
, sock
->proxies
[px
].username
, bytes
);
357 bytes
= strlen(sock
->proxies
[px
].password
);
359 memcpy(p
, sock
->proxies
[px
].password
, 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
;
367 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_PROXY_UNEXPECTED_RESPONSE
, ROCKSOCK_FILENAME
, __LINE__
);
369 } else if(socksdata
[1] != 0) {
370 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_PROXY_AUTH_FAILED
, ROCKSOCK_FILENAME
, __LINE__
);
374 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_PROXY_AUTH_FAILED
, ROCKSOCK_FILENAME
, __LINE__
);
382 if(isnumericipv4(targetproxy
->hostinfo
.host
)) {
383 *p
++ = 1; // ipv4 method
385 ipv4fromstring(targetproxy
->hostinfo
.host
, (unsigned char*) p
);
387 *p
++ = 3; //hostname method, requires the server to do dns lookups.
388 bytes
= strlen(targetproxy
->hostinfo
.host
);
390 return rocksock_seterror(sock
, RS_ET_OWN
, RS_E_SOCKS5_AUTH_EXCEEDSIZE
, ROCKSOCK_FILENAME
, __LINE__
);
392 memcpy(p
, targetproxy
->hostinfo
.host
, 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
;
403 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_PROXY_UNEXPECTED_RESPONSE
, ROCKSOCK_FILENAME
, __LINE__
);
406 switch(socksdata
[1]) {
410 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_PROXY_GENERAL_FAILURE
, ROCKSOCK_FILENAME
, __LINE__
);
413 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_PROXY_AUTH_FAILED
, ROCKSOCK_FILENAME
, __LINE__
);
416 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_TARGETPROXY_NET_UNREACHABLE
, ROCKSOCK_FILENAME
, __LINE__
);
419 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_TARGETPROXY_HOST_UNREACHABLE
, ROCKSOCK_FILENAME
, __LINE__
);
422 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_TARGETPROXY_CONN_REFUSED
, ROCKSOCK_FILENAME
, __LINE__
);
425 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_TARGETPROXY_TTL_EXPIRED
, ROCKSOCK_FILENAME
, __LINE__
);
428 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_PROXY_COMMAND_NOT_SUPPORTED
, ROCKSOCK_FILENAME
, __LINE__
);
431 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_PROXY_ADDRESSTYPE_NOT_SUPPORTED
, ROCKSOCK_FILENAME
, __LINE__
);
434 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_PROXY_UNEXPECTED_RESPONSE
, ROCKSOCK_FILENAME
, __LINE__
);
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
;
445 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_PROXY_UNEXPECTED_RESPONSE
, ROCKSOCK_FILENAME
, __LINE__
);
448 if(socksdata
[9] != '2') {
449 ret
= rocksock_seterror(sock
, RS_ET_OWN
, RS_E_TARGETPROXY_CONNECT_FAILED
, ROCKSOCK_FILENAME
, __LINE__
);
460 ret
= rocksock_ssl_connect_fd(sock
);
464 return rocksock_seterror(sock
, RS_ET_OWN
, 0, NULL
, 0);
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__
);
481 size_t bytesleft
= bufsize
? bufsize
: strlen(buffer
);
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
;
490 if(operation
== RS_OT_SEND
)
491 ret
= setsockopt(sock
->socket
, SOL_SOCKET
, SO_SNDTIMEO
, (void*) make_timeval(&tv
, sock
->timeout
), sizeof(tv
));
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__
);
499 byteswanted
= (chunksize
&& chunksize
< bytesleft
) ? chunksize
: bytesleft
;
502 if(operation
== RS_OT_SEND
)
503 ret
= rocksock_ssl_send(sock
, bufptr
, byteswanted
);
505 ret
= rocksock_ssl_recv(sock
, bufptr
, byteswanted
);
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
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
);
522 ret
= recv(sock
->socket
, bufptr
, byteswanted
, 0);
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__
);
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__
);
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
;
555 rocksock_ssl_free_context(sock
);
557 if(sock
->socket
!= -1) close(sock
->socket
);
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;
566 return rocksock_seterror(sock
, RS_ET_OWN
, 0, NULL
, 0);