TODO: How High The Moon
[s-mailx.git] / socket.c
blobfe4985ec4cac1fd2ef1ec1b60116622d29d9db65
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Socket operations.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8 /*
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
33 #undef n_FILE
34 #define n_FILE socket
36 #ifndef HAVE_AMALGAMATION
37 # include "nail.h"
38 #endif
40 EMPTY_FILE()
41 #ifdef HAVE_SOCKETS
42 # ifdef HAVE_NONBLOCKSOCK
43 /*# include <sys/types.h>*/
44 # include <sys/select.h>
45 /*# include <sys/time.h>*/
46 # include <arpa/inet.h>
47 /*# include <netinet/in.h>*/
48 /*# include <errno.h>*/
49 /*# include <fcntl.h>*/
50 /*# include <stdlib.h>*/
51 /*# include <unistd.h>*/
52 # endif
54 #include <sys/socket.h>
56 #include <netdb.h>
58 #include <netinet/in.h>
60 #ifdef HAVE_ARPA_INET_H
61 # include <arpa/inet.h>
62 #endif
64 #ifdef HAVE_XTLS
65 # include <openssl/err.h>
66 # include <openssl/rand.h>
67 # include <openssl/ssl.h>
68 # include <openssl/x509v3.h>
69 # include <openssl/x509.h>
70 #endif
72 /* */
73 static bool_t a_socket_open(struct sock *sp, struct url *urlp);
75 /* */
76 static int a_socket_connect(int fd, struct sockaddr *soap, size_t soapl);
78 /* Write to socket fd, restarting on EINTR, unless anything is written */
79 static long a_socket_xwrite(int fd, char const *data, size_t sz);
81 static sigjmp_buf __sopen_actjmp; /* TODO someday, we won't need it no more */
82 static int __sopen_sig; /* TODO someday, we won't need it no more */
83 static void
84 __sopen_onsig(int sig) /* TODO someday, we won't need it no more */
86 NYD_X; /* Signal handler */
87 if (__sopen_sig == -1) {
88 fprintf(n_stderr, _("\nInterrupting this operation may turn "
89 "the DNS resolver unusable\n"));
90 __sopen_sig = 0;
91 } else {
92 __sopen_sig = sig;
93 siglongjmp(__sopen_actjmp, 1);
97 static bool_t
98 a_socket_open(struct sock *sp, struct url *urlp) /* TODO sigstuff; refactor */
100 # ifdef HAVE_SO_XTIMEO
101 struct timeval tv;
102 # endif
103 # ifdef HAVE_SO_LINGER
104 struct linger li;
105 # endif
106 # ifdef HAVE_GETADDRINFO
107 # ifndef NI_MAXHOST
108 # define NI_MAXHOST 1025
109 # endif
110 char hbuf[NI_MAXHOST];
111 struct addrinfo hints, *res0 = NULL, *res;
112 # else
113 struct sockaddr_in servaddr;
114 struct in_addr **pptr;
115 struct hostent *hp;
116 struct servent *ep;
117 # endif
118 sighandler_type volatile ohup, oint;
119 char const * volatile serv;
120 int volatile sofd = -1, errval;
121 NYD2_ENTER;
123 memset(sp, 0, sizeof *sp);
124 n_UNINIT(errval, 0);
126 serv = (urlp->url_port != NULL) ? urlp->url_port : urlp->url_proto;
128 if (n_poption & n_PO_D_V)
129 n_err(_("Resolving host %s:%s ... "), urlp->url_host.s, serv);
131 /* Signal handling (in respect to __sopen_sig dealing) is heavy, but no
132 * healing until v15.0 and i want to end up with that functionality */
133 hold_sigs();
134 __sopen_sig = 0;
135 ohup = safe_signal(SIGHUP, &__sopen_onsig);
136 oint = safe_signal(SIGINT, &__sopen_onsig);
137 if (sigsetjmp(__sopen_actjmp, 0)) {
138 jpseudo_jump:
139 n_err("%s\n",
140 (__sopen_sig == SIGHUP ? _("Hangup") : _("Interrupted")));
141 if (sofd >= 0) {
142 close(sofd);
143 sofd = -1;
145 goto jjumped;
147 rele_sigs();
149 # ifdef HAVE_GETADDRINFO
150 for (;;) {
151 memset(&hints, 0, sizeof hints);
152 hints.ai_socktype = SOCK_STREAM;
153 __sopen_sig = -1;
154 errval = getaddrinfo(urlp->url_host.s, serv, &hints, &res0);
155 if (__sopen_sig != -1) {
156 __sopen_sig = SIGINT;
157 goto jpseudo_jump;
159 __sopen_sig = 0;
160 if (errval == 0)
161 break;
163 if (n_poption & n_PO_D_V)
164 n_err(_("failed\n"));
165 n_err(_("Lookup of %s:%s failed: %s\n"),
166 urlp->url_host.s, serv, gai_strerror(errval));
168 /* Error seems to depend on how "smart" the /etc/service code is: is it
169 * "able" to state whether the service as such is NONAME or does it only
170 * check for the given ai_socktype.. */
171 if (errval == EAI_NONAME || errval == EAI_SERVICE) {
172 if (serv == urlp->url_proto &&
173 (serv = n_servbyname(urlp->url_proto, NULL)) != NULL &&
174 *serv != '\0') {
175 n_err(_(" Trying standard protocol port %s\n"), serv);
176 n_err(_(" If that succeeds consider including the "
177 "port in the URL!\n"));
178 continue;
180 if (serv != urlp->url_port)
181 n_err(_(" Including a port number in the URL may "
182 "circumvent this problem\n"));
184 assert(sofd == -1);
185 errval = 0;
186 goto jjumped;
188 if (n_poption & n_PO_D_V)
189 n_err(_("done\n"));
191 for (res = res0; res != NULL && sofd < 0; res = res->ai_next) {
192 if (n_poption & n_PO_D_V) {
193 if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof hbuf,
194 NULL, 0, NI_NUMERICHOST))
195 memcpy(hbuf, "unknown host", sizeof("unknown host"));
196 n_err(_("%sConnecting to %s:%s ... "),
197 (res == res0 ? n_empty : "\n"), hbuf, serv);
200 sofd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
201 if(sofd >= 0 &&
202 (errval = a_socket_connect(sofd, res->ai_addr, res->ai_addrlen)
203 ) != n_ERR_NONE)
204 sofd = -1;
207 jjumped:
208 if (res0 != NULL) {
209 freeaddrinfo(res0);
210 res0 = NULL;
213 # else /* HAVE_GETADDRINFO */
214 if (serv == urlp->url_proto) {
215 if ((ep = getservbyname(n_UNCONST(serv), "tcp")) != NULL)
216 urlp->url_portno = ntohs(ep->s_port);
217 else {
218 if (n_poption & n_PO_D_V)
219 n_err(_("failed\n"));
220 if ((serv = n_servbyname(urlp->url_proto, &urlp->url_portno)) != NULL)
221 n_err(_(" Unknown service: %s\n"), urlp->url_proto);
222 n_err(_(" Trying standard protocol port %s\n"), serv);
223 n_err(_(" If that succeeds consider including the "
224 "port in the URL!\n"));
225 else {
226 n_err(_(" Unknown service: %s\n"), urlp->url_proto);
227 n_err(_(" Including a port number in the URL may "
228 "circumvent this problem\n"));
229 assert(sofd == -1 && errval == 0);
230 goto jjumped;
235 __sopen_sig = -1;
236 hp = gethostbyname(urlp->url_host.s);
237 if (__sopen_sig != -1) {
238 __sopen_sig = SIGINT;
239 goto jpseudo_jump;
241 __sopen_sig = 0;
243 if (hp == NULL) {
244 char const *emsg;
246 if (n_poption & n_PO_D_V)
247 n_err(_("failed\n"));
248 switch (h_errno) {
249 case HOST_NOT_FOUND: emsg = N_("host not found"); break;
250 default:
251 case TRY_AGAIN: emsg = N_("(maybe) try again later"); break;
252 case NO_RECOVERY: emsg = N_("non-recoverable server error"); break;
253 case NO_DATA: emsg = N_("valid name without IP address"); break;
255 n_err(_("Lookup of %s:%s failed: %s\n"),
256 urlp->url_host.s, serv, V_(emsg));
257 goto jjumped;
258 } else if (n_poption & n_PO_D_V)
259 n_err(_("done\n"));
261 pptr = (struct in_addr**)hp->h_addr_list;
262 if ((sofd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
263 n_perr(_("could not create socket"), 0);
264 assert(sofd == -1 && errval == 0);
265 goto jjumped;
268 memset(&servaddr, 0, sizeof servaddr);
269 servaddr.sin_family = AF_INET;
270 servaddr.sin_port = htons(urlp->url_portno);
271 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
272 if (n_poption & n_PO_D_V)
273 n_err(_("%sConnecting to %s:%d ... "),
274 n_empty, inet_ntoa(**pptr), (int)urlp->url_portno);
275 if((errval = a_socket_connect(sofd, (struct sockaddr*)&servaddr,
276 sizeof servaddr)) != n_ERR_NONE)
277 sofd = -1;
278 jjumped:
279 # endif /* !HAVE_GETADDRINFO */
281 hold_sigs();
282 safe_signal(SIGINT, oint);
283 safe_signal(SIGHUP, ohup);
284 rele_sigs();
286 if (sofd < 0) {
287 if (errval != 0) {
288 n_perr(_("Could not connect"), errval);
289 n_err_no = errval;
291 goto jleave;
294 sp->s_fd = sofd;
295 if (n_poption & n_PO_D_V)
296 n_err(_("connected.\n"));
298 /* And the regular timeouts XXX configurable */
299 # ifdef HAVE_SO_XTIMEO
300 tv.tv_sec = 42;
301 tv.tv_usec = 0;
302 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
303 (void)setsockopt(sofd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
304 # endif
305 # ifdef HAVE_SO_LINGER
306 li.l_onoff = 1;
307 li.l_linger = 42;
308 (void)setsockopt(sofd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
309 # endif
311 /* SSL/TLS upgrade? */
312 # ifdef HAVE_TLS
313 hold_sigs();
315 # if defined HAVE_GETADDRINFO && defined SSL_CTRL_SET_TLSEXT_HOSTNAME /* TODO
316 * TODO the SSL_ def check should NOT be here */
317 if(urlp->url_flags & n_URL_TLS_MASK){
318 memset(&hints, 0, sizeof hints);
319 hints.ai_family = AF_UNSPEC;
320 hints.ai_flags = AI_NUMERICHOST;
321 res0 = NULL;
322 if(getaddrinfo(urlp->url_host.s, NULL, &hints, &res0) == 0)
323 freeaddrinfo(res0);
324 else
325 urlp->url_flags |= n_URL_HOST_IS_NAME;
327 # endif
329 if (urlp->url_flags & n_URL_TLS_REQUIRED) {
330 ohup = safe_signal(SIGHUP, &__sopen_onsig);
331 oint = safe_signal(SIGINT, &__sopen_onsig);
332 if (sigsetjmp(__sopen_actjmp, 0)) {
333 n_err(_("%s during SSL/TLS handshake\n"),
334 (__sopen_sig == SIGHUP ? _("Hangup") : _("Interrupted")));
335 goto jsclose;
337 rele_sigs();
339 if(!n_tls_open(urlp, sp)){
340 jsclose:
341 sclose(sp);
342 sofd = -1;
343 }else if(urlp->url_cproto == CPROTO_CERTINFO)
344 sclose(sp);
346 hold_sigs();
347 safe_signal(SIGINT, oint);
348 safe_signal(SIGHUP, ohup);
351 rele_sigs();
352 # endif /* HAVE_TLS */
354 jleave:
355 /* May need to bounce the signal to the go.c trampoline (or wherever) */
356 if (__sopen_sig != 0) {
357 sigset_t cset;
358 sigemptyset(&cset);
359 sigaddset(&cset, __sopen_sig);
360 sigprocmask(SIG_UNBLOCK, &cset, NULL);
361 n_raise(__sopen_sig);
363 NYD2_LEAVE;
364 return (sofd >= 0);
367 static int
368 a_socket_connect(int fd, struct sockaddr *soap, size_t soapl){
369 int rv;
370 NYD_ENTER;
372 #ifdef HAVE_NONBLOCKSOCK
373 rv = fcntl(fd, F_GETFL, 0);
374 if(rv != -1 && !fcntl(fd, F_SETFL, rv | O_NONBLOCK)){
375 fd_set fdset;
376 struct timeval tv; /* XXX configurable */
377 socklen_t sol;
378 int i, soe;
380 if(connect(fd, soap, soapl) && (i = n_err_no) != n_ERR_INPROGRESS){
381 rv = i;
382 goto jerr_noerrno;
385 FD_ZERO(&fdset);
386 FD_SET(fd, &fdset);
387 if(n_poption & n_PO_D_V){
388 i = 21;
389 tv.tv_sec = 2;
390 }else{
391 i = 1;
392 tv.tv_sec = 42;
394 tv.tv_usec = 0;
395 jrewait:
396 if((soe = select(fd + 1, NULL, &fdset, NULL, &tv)) == 1){
397 i = rv;
398 sol = sizeof rv;
399 getsockopt(fd, SOL_SOCKET, SO_ERROR, &rv, &sol);
400 fcntl(fd, F_SETFL, i);
401 if(n_poption & n_PO_D_V)
402 n_err(" ");
403 }else if(soe == 0){
404 if((n_poption & n_PO_D_V) && --i > 0){
405 n_err(".");
406 tv.tv_sec = 2;
407 tv.tv_usec = 0;
408 goto jrewait;
410 n_err(_(" timeout\n"));
411 close(fd);
412 rv = n_ERR_TIMEDOUT;
413 }else
414 goto jerr;
415 }else
416 #endif /* HAVE_NONBLOCKSOCK */
418 if(!connect(fd, soap, soapl))
419 rv = n_ERR_NONE;
420 else{
421 #ifdef HAVE_NONBLOCKSOCK
422 jerr:
423 #endif
424 rv = n_err_no;
425 #ifdef HAVE_NONBLOCKSOCK
426 jerr_noerrno:
427 #endif
428 n_perr(_("connect(2) failed:"), rv);
429 close(fd);
431 NYD_LEAVE;
432 return rv;
435 static long
436 a_socket_xwrite(int fd, char const *data, size_t sz)
438 long rv = -1, wo;
439 size_t wt = 0;
440 NYD_ENTER;
442 do {
443 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
444 if (n_err_no == n_ERR_INTR)
445 continue;
446 else
447 goto jleave;
449 wt += wo;
450 } while (wt < sz);
451 rv = (long)sz;
452 jleave:
453 NYD_LEAVE;
454 return rv;
457 FL int
458 sclose(struct sock *sp)
460 int i;
461 NYD_ENTER;
463 i = sp->s_fd;
464 sp->s_fd = -1;
465 /* TODO NOTE: we MUST NOT close the descriptor 0 here...
466 * TODO of course this should be handled in a VMAILFS->open() .s_fd=-1,
467 * TODO but unfortunately it isn't yet */
468 if (i <= 0)
469 i = 0;
470 else {
471 if (sp->s_onclose != NULL)
472 (*sp->s_onclose)();
473 if (sp->s_wbuf != NULL)
474 n_free(sp->s_wbuf);
475 # ifdef HAVE_XTLS
476 if (sp->s_use_tls) {
477 void *s_tls = sp->s_tls;
479 sp->s_tls = NULL;
480 sp->s_use_tls = 0;
481 while (!SSL_shutdown(s_tls)) /* XXX proper error handling;signals! */
483 SSL_free(s_tls);
485 # endif
486 i = close(i);
488 NYD_LEAVE;
489 return i;
492 FL enum okay
493 swrite(struct sock *sp, char const *data)
495 enum okay rv;
496 NYD2_ENTER;
498 rv = swrite1(sp, data, strlen(data), 0);
499 NYD2_LEAVE;
500 return rv;
503 FL enum okay
504 swrite1(struct sock *sp, char const *data, int sz, int use_buffer)
506 enum okay rv = STOP;
507 int x;
508 NYD2_ENTER;
510 if (use_buffer > 0) {
511 int di;
513 if (sp->s_wbuf == NULL) {
514 sp->s_wbufsize = 4096;
515 sp->s_wbuf = n_alloc(sp->s_wbufsize);
516 sp->s_wbufpos = 0;
518 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
519 di = sp->s_wbufsize - sp->s_wbufpos;
520 sz -= di;
521 if (sp->s_wbufpos > 0) {
522 memcpy(sp->s_wbuf + sp->s_wbufpos, data, di);
523 rv = swrite1(sp, sp->s_wbuf, sp->s_wbufsize, -1);
524 } else
525 rv = swrite1(sp, data, sp->s_wbufsize, -1);
526 if (rv != OKAY)
527 goto jleave;
528 data += di;
529 sp->s_wbufpos = 0;
531 if (sz == sp->s_wbufsize) {
532 rv = swrite1(sp, data, sp->s_wbufsize, -1);
533 if (rv != OKAY)
534 goto jleave;
535 } else if (sz) {
536 memcpy(sp->s_wbuf+ sp->s_wbufpos, data, sz);
537 sp->s_wbufpos += sz;
539 rv = OKAY;
540 goto jleave;
541 } else if (use_buffer == 0 && sp->s_wbuf != NULL && sp->s_wbufpos > 0) {
542 x = sp->s_wbufpos;
543 sp->s_wbufpos = 0;
544 if ((rv = swrite1(sp, sp->s_wbuf, x, -1)) != OKAY)
545 goto jleave;
547 if (sz == 0) {
548 rv = OKAY;
549 goto jleave;
552 # ifdef HAVE_XTLS
553 if (sp->s_use_tls) {
554 jssl_retry:
555 x = SSL_write(sp->s_tls, data, sz);
556 if (x < 0) {
557 switch (SSL_get_error(sp->s_tls, x)) {
558 case SSL_ERROR_WANT_READ:
559 case SSL_ERROR_WANT_WRITE:
560 goto jssl_retry;
563 } else
564 # endif
566 x = a_socket_xwrite(sp->s_fd, data, sz);
568 if (x != sz) {
569 char o[512];
571 snprintf(o, sizeof o, "%s write error",
572 (sp->s_desc ? sp->s_desc : "socket"));
573 # ifdef HAVE_XTLS
574 if (sp->s_use_tls)
575 ssl_gen_err("%s", o);
576 else
577 # endif
578 n_perr(o, 0);
579 if (x < 0)
580 sclose(sp);
581 rv = STOP;
582 goto jleave;
584 rv = OKAY;
585 jleave:
586 NYD2_LEAVE;
587 return rv;
590 FL bool_t
591 sopen(struct sock *sp, struct url *urlp){
592 char const *cp;
593 bool_t rv;
594 NYD_ENTER;
596 rv = FAL0;
598 /* We may have a proxy configured */
599 if((cp = xok_vlook(socks_proxy, urlp, OXM_ALL)) == NULL)
600 rv = a_socket_open(sp, urlp);
601 else{
602 ui8_t pbuf[4 + 1 + 255 + 2];
603 size_t i;
604 char const *emsg;
605 struct url url2;
607 if(!url_parse(&url2, CPROTO_SOCKS, cp)){
608 n_err(_("Failed to parse *socks-proxy*: %s\n"), cp);
609 goto jleave;
611 if(urlp->url_host.l > 255){
612 n_err(_("*socks-proxy*: hostname too long: %s\n"),
613 urlp->url_input);
614 goto jleave;
617 if (n_poption & n_PO_D_V)
618 n_err(_("Connecting via *socks-proxy* to %s:%s ...\n"),
619 urlp->url_host.s,
620 (urlp->url_port != NULL ? urlp->url_port : urlp->url_proto));
622 if(!a_socket_open(sp, &url2)){
623 n_err(_("Failed to connect to *socks-proxy*: %s\n"), cp);
624 goto jleave;
627 /* RFC 1928: version identifier/method selection message */
628 pbuf[0] = 0x05; /* VER: protocol version: X'05' */
629 pbuf[1] = 0x01; /* NMETHODS: 1 */
630 pbuf[2] = 0x00; /* METHOD: X'00' NO AUTHENTICATION REQUIRED */
631 if(write(sp->s_fd, pbuf, 3) != 3){
632 jerrsocks:
633 n_perr("*socks-proxy*", 0);
634 jesocks:
635 sclose(sp);
636 goto jleave;
639 /* Receive greeting */
640 if(read(sp->s_fd, pbuf, 2) != 2)
641 goto jerrsocks;
642 if(pbuf[0] != 0x05 || pbuf[1] != 0x00){
643 jesocksreply:
644 emsg = N_("unexpected reply\n");
645 jesocksreplymsg:
646 /* I18N: error message and failing URL */
647 n_err(_("*socks-proxy*: %s: %s\n"), V_(emsg), cp);
648 goto jesocks;
651 /* RFC 1928: CONNECT request */
652 pbuf[0] = 0x05; /* VER: protocol version: X'05' */
653 pbuf[1] = 0x01; /* CMD: CONNECT X'01' */
654 pbuf[2] = 0x00; /* RESERVED */
655 pbuf[3] = 0x03; /* ATYP: domain name */
656 pbuf[4] = (ui8_t)urlp->url_host.l;
657 memcpy(&pbuf[i = 5], urlp->url_host.s, urlp->url_host.l);
658 /* C99 */{
659 ui16_t x;
661 x = htons(urlp->url_portno);
662 memcpy(&pbuf[i += urlp->url_host.l], (ui8_t*)&x, sizeof x);
663 i += sizeof x;
665 if(write(sp->s_fd, pbuf, i) != (ssize_t)i)
666 goto jerrsocks;
668 /* Connect result */
669 if((i = read(sp->s_fd, pbuf, 4)) != 4)
670 goto jerrsocks;
671 /* Version 5, reserved must be 0 */
672 if(pbuf[0] != 0x05 || pbuf[2] != 0x00)
673 goto jesocksreply;
674 /* Result */
675 switch(pbuf[1]){
676 case 0x00: emsg = NULL; break;
677 case 0x01: emsg = N_("SOCKS server failure"); break;
678 case 0x02: emsg = N_("connection not allowed by ruleset"); break;
679 case 0x03: emsg = N_("network unreachable"); break;
680 case 0x04: emsg = N_("host unreachable"); break;
681 case 0x05: emsg = N_("connection refused"); break;
682 case 0x06: emsg = N_("TTL expired"); break;
683 case 0x07: emsg = N_("command not supported"); break;
684 case 0x08: emsg = N_("address type not supported"); break;
685 default: emsg = N_("unknown SOCKS error code"); break;
687 if(emsg != NULL)
688 goto jesocksreplymsg;
690 /* Address type variable; read the BND.PORT with it.
691 * This is actually false since RFC 1928 says that the BND.ADDR reply to
692 * CONNECT contains the IP address, so only 0x01 and 0x04 are allowed */
693 switch(pbuf[3]){
694 case 0x01: i = 4; break;
695 case 0x03: i = 1; break;
696 case 0x04: i = 16; break;
697 default: goto jesocksreply;
699 i += sizeof(ui16_t);
700 if(read(sp->s_fd, pbuf, i) != (ssize_t)i)
701 goto jerrsocks;
702 if(i == 1 + sizeof(ui16_t)){
703 i = pbuf[0];
704 if(read(sp->s_fd, pbuf, i) != (ssize_t)i)
705 goto jerrsocks;
707 rv = TRU1;
709 jleave:
710 NYD_LEAVE;
711 return rv;
714 FL int
715 (sgetline)(char **line, size_t *linesize, size_t *linelen, struct sock *sp
716 n_MEMORY_DEBUG_ARGS)
718 int rv;
719 size_t lsize;
720 char *lp_base, *lp;
721 NYD2_ENTER;
723 lsize = *linesize;
724 lp_base = *line;
725 lp = lp_base;
727 if (sp->s_rsz < 0) {
728 sclose(sp);
729 rv = sp->s_rsz;
730 goto jleave;
733 do {
734 if (lp_base == NULL || PTRCMP(lp, >, lp_base + lsize - 128)) {
735 size_t diff = PTR2SIZE(lp - lp_base);
736 *linesize = (lsize += 256); /* XXX magic */
737 *line = lp_base = (n_realloc)(lp_base, lsize n_MEMORY_DEBUG_ARGSCALL);
738 lp = lp_base + diff;
741 if (sp->s_rbufptr == NULL ||
742 PTRCMP(sp->s_rbufptr, >=, sp->s_rbuf + sp->s_rsz)) {
743 # ifdef HAVE_XTLS
744 if (sp->s_use_tls) {
745 jssl_retry:
746 sp->s_rsz = SSL_read(sp->s_tls, sp->s_rbuf, sizeof sp->s_rbuf);
747 if (sp->s_rsz <= 0) {
748 if (sp->s_rsz < 0) {
749 char o[512];
751 switch(SSL_get_error(sp->s_tls, sp->s_rsz)) {
752 case SSL_ERROR_WANT_READ:
753 case SSL_ERROR_WANT_WRITE:
754 goto jssl_retry;
756 snprintf(o, sizeof o, "%s",
757 (sp->s_desc ? sp->s_desc : "socket"));
758 ssl_gen_err("%s", o);
760 break;
762 } else
763 # endif
765 jagain:
766 sp->s_rsz = read(sp->s_fd, sp->s_rbuf, sizeof sp->s_rbuf);
767 if (sp->s_rsz <= 0) {
768 if (sp->s_rsz < 0) {
769 char o[512];
771 if (n_err_no == n_ERR_INTR)
772 goto jagain;
773 snprintf(o, sizeof o, "%s",
774 (sp->s_desc ? sp->s_desc : "socket"));
775 n_perr(o, 0);
777 break;
780 sp->s_rbufptr = sp->s_rbuf;
782 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
783 *lp = '\0';
784 lsize = PTR2SIZE(lp - lp_base);
786 if (linelen)
787 *linelen = lsize;
788 rv = (int)lsize;
789 jleave:
790 NYD2_LEAVE;
791 return rv;
793 #endif /* HAVE_SOCKETS */
795 /* s-it-mode */