1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2  *@ Socket operations.
3  *
4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5  * Copyright (c) 2012 - 2020 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.
20  *
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.
32  */
33 #undef su_FILE
34 #define su_FILE net_socket
35 #define mx_SOURCE
36 
37 #ifndef mx_HAVE_AMALGAMATION
38 # include "mx/nail.h"
39 #endif
40 
41 su_EMPTY_FILE()
42 #ifdef mx_HAVE_NET
43 # ifdef mx_HAVE_NONBLOCKSOCK
44 /*#  include <sys/types.h>*/
45 #  include <sys/select.h>
46 /*#  include <sys/time.h>*/
47 #  include <arpa/inet.h>
48 /*#  include <netinet/in.h>*/
49 /*#  include <errno.h>*/
50 /*#  include <fcntl.h>*/
51 /*#  include <stdlib.h>*/
52 /*#  include <unistd.h>*/
53 
54 #  include <su/icodec.h>
55 # endif
56 
57 #include <sys/socket.h>
58 
59 #include <netdb.h>
60 #ifdef mx_HAVE_ARPA_INET_H
61 # include <arpa/inet.h>
62 #endif
63 #include <netinet/in.h>
64 
65 #ifdef mx_HAVE_XTLS
66 # include <openssl/err.h>
67 # include <openssl/rand.h>
68 # include <openssl/ssl.h>
69 # include <openssl/x509v3.h>
70 # include <openssl/x509.h>
71 #endif
72 
73 #include <su/cs.h>
74 #include <su/mem.h>
75 
76 #include "mx/sigs.h"
77 #include "mx/url.h"
78 
79 #include "mx/net-socket.h"
80 /* TODO fake */
81 #include "su/code-in.h"
82 
83 /* If a_netso_sig was zeroed .. test if anything happened */
84 static void a_netso_onsig(int sig); /* TODO someday, need no more */
85 static void a_netso_test_sig(void);
86 
87 /* */
88 static boole a_netso_open(struct mx_socket *sop, struct mx_url *urlp);
89 
90 /* .url_flags&URL_TLS_MASK, do what is to be done; closes socket on error! */
91 #ifdef mx_HAVE_TLS
92 static int a_netso_open_tls_maybe(struct mx_socket *sop, struct mx_url *urlp);
93 #endif
94 
95 /* */
96 static int a_netso_connect(int fd, struct sockaddr *soap, uz soapl);
97 
98 /* Write to socket fd, restarting on EINTR, unless anything is written */
99 static long a_netso_xwrite(int fd, char const *data, uz size);
100 
101 static sigjmp_buf a_netso_actjmp; /* TODO someday, we won't need it no more */
102 static int a_netso_sig; /* TODO someday, we won't need it no more */
103 
104 static void
a_netso_onsig(int sig)105 a_netso_onsig(int sig) /* TODO someday, we won't need it no more */
106 {
107    NYD; /* Signal handler */
108    if (a_netso_sig == -1) {
109       fprintf(n_stderr, _("\nInterrupting this operation may turn "
110          "the DNS resolver unusable\n"));
111       a_netso_sig = 0;
112    } else {
113       a_netso_sig = sig;
114       siglongjmp(a_netso_actjmp, 1);
115    }
116 }
117 
118 static void
a_netso_test_sig(void)119 a_netso_test_sig(void){
120    /* May need to bounce the signal to the go.c trampoline (or wherever) */
121    if(a_netso_sig != 0){
122       sigset_t cset;
123 
124       sigemptyset(&cset);
125       sigaddset(&cset, a_netso_sig);
126       sigprocmask(SIG_UNBLOCK, &cset, NIL);
127       n_raise(a_netso_sig);
128    }
129 }
130 
131 static boole
a_netso_open(struct mx_socket * sop,struct mx_url * urlp)132 a_netso_open(struct mx_socket *sop, struct mx_url *urlp) /*TODO sigs;refactor*/
133 {
134 #ifdef mx_HAVE_SO_XTIMEO
135    struct timeval tv;
136 #endif
137 #ifdef mx_HAVE_SO_LINGER
138    struct linger li;
139 #endif
140 #ifdef mx_HAVE_GETADDRINFO
141 # ifndef NI_MAXHOST
142 #  define NI_MAXHOST 1025
143 # endif
144    char hbuf[NI_MAXHOST];
145    struct addrinfo hints, *res0 = NULL, *res;
146 #else
147    struct sockaddr_in servaddr;
148    struct in_addr **pptr;
149    struct hostent *hp;
150    struct servent *ep;
151 #endif
152    n_sighdl_t volatile ohup, oint;
153    char const * volatile serv;
154    int volatile sofd = -1, errval;
155    NYD2_IN;
156 
157    su_mem_set(sop, 0, sizeof *sop);
158    UNINIT(errval, 0);
159 
160    serv = (urlp->url_port != NULL) ? urlp->url_port : urlp->url_proto;
161 
162    if (n_poption & n_PO_D_V)
163       n_err(_("Resolving host %s:%s ... "), urlp->url_host.s, serv);
164 
165    /* Signal handling (in respect to a_netso_sig dealing) is heavy, but no
166     * healing until v15.0 and i want to end up with that functionality */
167    hold_sigs();
168    a_netso_sig = 0;
169    ohup = safe_signal(SIGHUP, &a_netso_onsig);
170    oint = safe_signal(SIGINT, &a_netso_onsig);
171    if (sigsetjmp(a_netso_actjmp, 0)) {
172 jpseudo_jump:
173       n_err("%s\n",
174          (a_netso_sig == SIGHUP ? _("Hangup") : _("Interrupted")));
175       if (sofd >= 0) {
176          close(sofd);
177          sofd = -1;
178       }
179       goto jjumped;
180    }
181    rele_sigs();
182 
183 #ifdef mx_HAVE_GETADDRINFO
184    for (;;) {
185       su_mem_set(&hints, 0, sizeof hints);
186       hints.ai_socktype = SOCK_STREAM;
187       a_netso_sig = -1;
188       errval = getaddrinfo(urlp->url_host.s, serv, &hints, &res0);
189       if (a_netso_sig != -1) {
190          a_netso_sig = SIGINT;
191          goto jpseudo_jump;
192       }
193       a_netso_sig = 0;
194       if (errval == 0)
195          break;
196 
197       if (n_poption & n_PO_D_V)
198          n_err(_("failed\n"));
199       n_err(_("Lookup of %s:%s failed: %s\n"),
200          urlp->url_host.s, serv, gai_strerror(errval));
201 
202       /* Error seems to depend on how "smart" the /etc/service code is: is it
203        * "able" to state whether the service as such is NONAME or does it only
204        * check for the given ai_socktype.. */
205       if (errval == EAI_NONAME || errval == EAI_SERVICE) {
206          if (serv == urlp->url_proto &&
207                (serv = mx_url_servbyname(urlp->url_proto, NIL, NIL)) != NIL &&
208                *serv != '\0') {
209             n_err(_("  Trying standard protocol port %s\n"), serv);
210             n_err(_("  If that succeeds consider including the "
211                "port in the URL!\n"));
212             continue;
213          }
214          if (serv != urlp->url_port)
215             n_err(_("  Including a port number in the URL may "
216                "circumvent this problem\n"));
217       }
218       ASSERT(sofd == -1);
219       errval = 0;
220       goto jjumped;
221    }
222    if (n_poption & n_PO_D_V)
223       n_err(_("done\n"));
224 
225    for (res = res0; res != NULL && sofd < 0; res = res->ai_next) {
226       if (n_poption & n_PO_D_V) {
227          if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof hbuf,
228                NULL, 0, NI_NUMERICHOST))
229             su_mem_copy(hbuf, "unknown host", sizeof("unknown host"));
230          n_err(_("%sConnecting to %s:%s ... "),
231                (res == res0 ? n_empty : "\n"), hbuf, serv);
232       }
233 
234       sofd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
235       if(sofd >= 0 &&
236             (errval = a_netso_connect(sofd, res->ai_addr, res->ai_addrlen)
237                ) != su_ERR_NONE)
238          sofd = -1;
239    }
240 
241 jjumped:
242    if (res0 != NULL) {
243       freeaddrinfo(res0);
244       res0 = NULL;
245    }
246 
247 #else /* mx_HAVE_GETADDRINFO */
248    if (serv == urlp->url_proto) {
249       if ((ep = getservbyname(n_UNCONST(serv), "tcp")) != NULL)
250          urlp->url_portno = ntohs(ep->s_port);
251       else {
252          if (n_poption & n_PO_D_V)
253             n_err(_("failed\n"));
254          if ((serv = mx_url_servbyname(urlp->url_proto, &urlp->url_portno, NIL)
255                ) != NIL && *serv != '\0')
256             n_err(_("  Unknown service: %s\n"), urlp->url_proto);
257             n_err(_("  Trying standard protocol port %s\n"), serv);
258             n_err(_("  If that succeeds consider including the "
259                "port in the URL!\n"));
260          else {
261             n_err(_("  Unknown service: %s\n"), urlp->url_proto);
262             n_err(_("  Including a port number in the URL may "
263                "circumvent this problem\n"));
264             ASSERT(sofd == -1 && errval == 0);
265             goto jjumped;
266          }
267       }
268    }
269 
270    a_netso_sig = -1;
271    hp = gethostbyname(urlp->url_host.s);
272    if (a_netso_sig != -1) {
273       a_netso_sig = SIGINT;
274       goto jpseudo_jump;
275    }
276    a_netso_sig = 0;
277 
278    if (hp == NULL) {
279       char const *emsg;
280 
281       if (n_poption & n_PO_D_V)
282          n_err(_("failed\n"));
283       switch (h_errno) {
284       case HOST_NOT_FOUND: emsg = N_("host not found"); break;
285       default:
286       case TRY_AGAIN:      emsg = N_("(maybe) try again later"); break;
287       case NO_RECOVERY:    emsg = N_("non-recoverable server error"); break;
288       case NO_DATA:        emsg = N_("valid name without IP address"); break;
289       }
290       n_err(_("Lookup of %s:%s failed: %s\n"),
291          urlp->url_host.s, serv, V_(emsg));
292       goto jjumped;
293    } else if (n_poption & n_PO_D_V)
294       n_err(_("done\n"));
295 
296    pptr = (struct in_addr**)hp->h_addr_list;
297    if ((sofd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
298       n_perr(_("could not create socket"), 0);
299       ASSERT(sofd == -1 && errval == 0);
300       goto jjumped;
301    }
302 
303    su_mem_set(&servaddr, 0, sizeof servaddr);
304    servaddr.sin_family = AF_INET;
305    servaddr.sin_port = htons(urlp->url_portno);
306    su_mem_copy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
307    if (n_poption & n_PO_D_V)
308       n_err(_("%sConnecting to %s:%d ... "),
309          n_empty, inet_ntoa(**pptr), (int)urlp->url_portno);
310    if((errval = a_netso_connect(sofd, (struct sockaddr*)&servaddr,
311          sizeof servaddr)) != su_ERR_NONE)
312       sofd = -1;
313 jjumped:
314 #endif /* !mx_HAVE_GETADDRINFO */
315 
316    hold_sigs();
317    safe_signal(SIGINT, oint);
318    safe_signal(SIGHUP, ohup);
319    rele_sigs();
320 
321    if(sofd < 0){
322       if(errval != 0){
323          if(!(n_poption & n_PO_D_V))
324             n_perr(_("Could not connect(2)"), errval);
325          su_err_set_no(errval);
326       }
327       goto jleave;
328    }
329 
330    sop->s_fd = sofd;
331    if (n_poption & n_PO_D_V)
332       n_err(_("connected.\n"));
333 
334    /* And the regular timeouts XXX configurable */
335 #ifdef mx_HAVE_SO_XTIMEO
336    tv.tv_sec = 42;
337    tv.tv_usec = 0;
338    (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
339    (void)setsockopt(sofd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
340 #endif
341 #ifdef mx_HAVE_SO_LINGER
342    li.l_onoff = 1;
343    li.l_linger = 42;
344    (void)setsockopt(sofd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
345 #endif
346 
347    /* SSL/TLS upgrade? */
348 #ifdef mx_HAVE_TLS
349    if(urlp->url_flags & mx_URL_TLS_MASK)
350       sofd = a_netso_open_tls_maybe(sop, urlp);
351 #endif
352 
353 jleave:
354    a_netso_test_sig();
355    NYD2_OU;
356    return (sofd >= 0);
357 }
358 
359 #ifdef mx_HAVE_TLS
360 static int
a_netso_open_tls_maybe(struct mx_socket * sop,struct mx_url * urlp)361 a_netso_open_tls_maybe(struct mx_socket *sop, struct mx_url *urlp){
362    n_sighdl_t volatile ohup, oint;
363    NYD2_IN;
364 
365    hold_sigs();
366    a_netso_sig = 0;
367 
368 # if defined mx_HAVE_GETADDRINFO && defined SSL_CTRL_SET_TLSEXT_HOSTNAME
369       /* TODO the SSL_ def check should NOT be here */
370    if(urlp->url_flags & mx_URL_TLS_MASK){
371       struct {struct addrinfo hints; struct addrinfo *res0;} x;
372 
373       su_mem_set(&x, 0, sizeof x);
374       x.hints.ai_family = AF_UNSPEC;
375       x.hints.ai_flags = AI_NUMERICHOST;
376       if(getaddrinfo(urlp->url_host.s, NIL, &x.hints, &x.res0) == 0)
377          freeaddrinfo(x.res0);
378       else
379          urlp->url_flags |= mx_URL_HOST_IS_NAME;
380    }
381 # endif
382 
383    if(urlp->url_flags & mx_URL_TLS_REQUIRED){
384       ohup = safe_signal(SIGHUP, &a_netso_onsig);
385       oint = safe_signal(SIGINT, &a_netso_onsig);
386       if(sigsetjmp(a_netso_actjmp, 0)){
387          n_err(_("%s during SSL/TLS handshake\n"),
388             (a_netso_sig == SIGHUP ? _("Hangup") : _("Interrupted")));
389          goto jsclose;
390       }
391       rele_sigs();
392 
393       if(!n_tls_open(urlp, sop)){
394 jsclose:
395          mx_socket_close(sop);
396          ASSERT(sop->s_fd == -1);
397       }
398 
399       hold_sigs();
400       safe_signal(SIGINT, oint);
401       safe_signal(SIGHUP, ohup);
402    }
403 
404    rele_sigs();
405 
406    NYD2_OU;
407    return sop->s_fd;
408 }
409 #endif /* mx_HAVE_TLS */
410 
411 static int
a_netso_connect(int fd,struct sockaddr * soap,uz soapl)412 a_netso_connect(int fd, struct sockaddr *soap, uz soapl){
413    int rv;
414    NYD_IN;
415 
416 #ifdef mx_HAVE_NONBLOCKSOCK
417    rv = fcntl(fd, F_GETFL, 0);
418    if(rv != -1 && !fcntl(fd, F_SETFL, rv | O_NONBLOCK)){
419       fd_set fdset;
420       struct timeval tv; /* XXX configurable */
421       socklen_t sol;
422       boole show_progress;
423       uz cnt;
424       int i, soe;
425 
426       /* Always select(2) even if it succeeds right away, since on at least
427        * SunOS/Solaris 5.9 SPARC it will cause failures (busy resources) */
428       if(connect(fd, soap, soapl) && (i = su_err_no()) != su_ERR_INPROGRESS){
429          rv = i;
430          goto jerr_noerrno;
431       }else{
432          show_progress = ((n_poption & n_PO_D_V) ||
433                ((n_psonce & n_PSO_INTERACTIVE) && !(n_pstate & n_PS_ROBOT)));
434 
435          FD_ZERO(&fdset);
436          FD_SET(fd, &fdset);
437          /* C99 */{
438             char const *cp;
439 
440             if((cp = ok_vlook(socket_connect_timeout)) == NIL ||
441                   (su_idec_uz_cp(&cnt, cp, 0, NIL), cnt < 2))
442                cnt = 42; /* XXX mx-config.h */
443 
444             if(show_progress){
445                tv.tv_sec = 2;
446                cnt >>= 1;
447             }else{
448                tv.tv_sec = (long)cnt; /* XXX */
449                cnt = 1;
450             }
451          }
452 
453 jrewait:
454          tv.tv_usec = 0;
455          if((soe = select(fd + 1, NIL, &fdset, NIL, &tv)) == 1){
456             i = rv;
457             sol = sizeof rv;
458             getsockopt(fd, SOL_SOCKET, SO_ERROR, &rv, &sol);
459             fcntl(fd, F_SETFL, i);
460             if(show_progress == TRUM1)
461                n_err(" ");
462          }else if(soe == 0){
463             if(show_progress && --cnt > 0){
464                show_progress = TRUM1;
465                n_err(".");
466                tv.tv_sec = 2;
467                goto jrewait;
468             }
469             n_err(_(" timeout\n"));
470             close(fd);
471             rv = su_ERR_TIMEDOUT;
472          }else
473             goto jerr;
474       }
475    }else
476 #endif /* mx_HAVE_NONBLOCKSOCK */
477          if(!connect(fd, soap, soapl))
478       rv = su_ERR_NONE;
479    else{
480 #ifdef mx_HAVE_NONBLOCKSOCK
481 jerr:
482 #endif
483       rv = su_err_no();
484 #ifdef mx_HAVE_NONBLOCKSOCK
485 jerr_noerrno:
486 #endif
487       if(n_poption & n_PO_D_V)
488          n_perr(_("connect(2) failed:"), rv);
489       close(fd);
490    }
491 
492    NYD_OU;
493    return rv;
494 }
495 
496 static long
a_netso_xwrite(int fd,char const * data,uz size)497 a_netso_xwrite(int fd, char const *data, uz size)
498 {
499    long rv = -1, wo;
500    uz wt = 0;
501    NYD_IN;
502 
503    do {
504       if ((wo = write(fd, data + wt, size - wt)) < 0) {
505          if (su_err_no() == su_ERR_INTR)
506             continue;
507          else
508             goto jleave;
509       }
510       wt += wo;
511    } while (wt < size);
512    rv = (long)size;
513 jleave:
514    NYD_OU;
515    return rv;
516 }
517 
518 boole
mx_socket_open(struct mx_socket * sop,struct mx_url * urlp)519 mx_socket_open(struct mx_socket *sop, struct mx_url *urlp){
520    char const *cp;
521    boole rv;
522    NYD_IN;
523 
524    rv = FAL0;
525 
526    /* We may have a proxy configured */
527    if((cp = xok_vlook(socks_proxy, urlp, OXM_ALL)) == NULL)
528       rv = a_netso_open(sop, urlp);
529    else{
530       u8 pbuf[4 + 1 + 255 + 2];
531       uz i;
532       char const *emsg;
533       struct mx_url url2;
534 
535       if(!mx_url_parse(&url2, CPROTO_SOCKS, cp)){
536          n_err(_("Failed to parse *socks-proxy*: %s\n"), cp);
537          goto jleave;
538       }
539       if(urlp->url_host.l > 255){
540          n_err(_("*socks-proxy*: hostname too long: %s\n"),
541             urlp->url_input);
542          goto jleave;
543       }
544 
545       if(n_poption & n_PO_D_V)
546          n_err(_("Connecting to *socks-proxy*=%s\n"), cp);
547       if(!a_netso_open(sop, &url2)){
548          n_err(_("Failed to connect to *socks-proxy*: %s\n"), cp);
549          goto jleave;
550       }
551 
552       /* RFC 1928: version identifier/method selection message */
553       pbuf[0] = 0x05; /* VER: protocol version: X'05' */
554       pbuf[1] = 0x01; /* NMETHODS: 1 */
555       pbuf[2] = 0x00; /* METHOD: X'00' NO AUTHENTICATION REQUIRED */
556       if(write(sop->s_fd, pbuf, 3) != 3){
557 jerrsocks:
558          n_perr("*socks-proxy*", 0);
559 jesocks:
560          mx_socket_close(sop);
561          goto jleave;
562       }
563 
564       /* Receive greeting */
565       if(read(sop->s_fd, pbuf, 2) != 2)
566          goto jerrsocks;
567       if(pbuf[0] != 0x05 || pbuf[1] != 0x00){
568 jesocksreply:
569          emsg = N_("unexpected reply\n");
570 jesocksreplymsg:
571          /* I18N: error message and failing URL */
572          n_err(_("*socks-proxy*: %s: %s\n"), V_(emsg), cp);
573          goto jesocks;
574       }
575 
576       /* RFC 1928: CONNECT request */
577       if(n_poption & n_PO_D_V)
578          n_err(_("Through *socks-proxy*, connecting to %s:%s ...\n"),
579             urlp->url_host.s,
580             (urlp->url_port != NULL ? urlp->url_port : urlp->url_proto));
581       pbuf[0] = 0x05; /* VER: protocol version: X'05' */
582       pbuf[1] = 0x01; /* CMD: CONNECT X'01' */
583       pbuf[2] = 0x00; /* RESERVED */
584       pbuf[3] = 0x03; /* ATYP: domain name */
585       pbuf[4] = (u8)urlp->url_host.l;
586       su_mem_copy(&pbuf[i = 5], urlp->url_host.s, urlp->url_host.l);
587       /* C99 */{
588          u16 x;
589 
590          x = htons(urlp->url_portno);
591          su_mem_copy(&pbuf[i += urlp->url_host.l], (u8*)&x, sizeof x);
592          i += sizeof x;
593       }
594       if(write(sop->s_fd, pbuf, i) != (sz)i)
595          goto jerrsocks;
596 
597       /* Connect result */
598       if((i = read(sop->s_fd, pbuf, 4)) != 4)
599          goto jerrsocks;
600       /* Version 5, reserved must be 0 */
601       if(pbuf[0] != 0x05 || pbuf[2] != 0x00)
602          goto jesocksreply;
603       /* Result */
604       switch(pbuf[1]){
605       case 0x00: emsg = NULL; break;
606       case 0x01: emsg = N_("SOCKS server failure"); break;
607       case 0x02: emsg = N_("connection not allowed by ruleset"); break;
608       case 0x03: emsg = N_("network unreachable"); break;
609       case 0x04: emsg = N_("host unreachable"); break;
610       case 0x05: emsg = N_("connection refused"); break;
611       case 0x06: emsg = N_("TTL expired"); break;
612       case 0x07: emsg = N_("command not supported"); break;
613       case 0x08: emsg = N_("address type not supported"); break;
614       default: emsg = N_("unknown SOCKS error code"); break;
615       }
616       if(emsg != NULL)
617          goto jesocksreplymsg;
618 
619       /* Address type variable; read the BND.PORT with it.
620        * This is actually false since RFC 1928 says that the BND.ADDR reply to
621        * CONNECT contains the IP address, so only 0x01 and 0x04 are allowed */
622       switch(pbuf[3]){
623       case 0x01: i = 4; break;
624       case 0x03: i = 1; break;
625       case 0x04: i = 16; break;
626       default: goto jesocksreply;
627       }
628       i += sizeof(u16);
629       if(read(sop->s_fd, pbuf, i) != (sz)i)
630          goto jerrsocks;
631       if(i == 1 + sizeof(u16)){
632          i = pbuf[0];
633          if(read(sop->s_fd, pbuf, i) != (sz)i)
634             goto jerrsocks;
635       }
636 
637       /* SSL/TLS upgrade? */
638 #ifdef mx_HAVE_TLS
639       if(urlp->url_flags & mx_URL_TLS_MASK)
640          rv = (a_netso_open_tls_maybe(sop, urlp) >= 0);
641       else
642 #endif
643          rv = TRU1;
644    }
645 jleave:
646    a_netso_test_sig();
647    NYD_OU;
648    return rv;
649 }
650 
651 int
mx_socket_close(struct mx_socket * sop)652 mx_socket_close(struct mx_socket *sop)
653 {
654    int i;
655    NYD_IN;
656 
657    i = sop->s_fd;
658    sop->s_fd = -1;
659    /* TODO NOTE: we MUST NOT close the descriptor 0 here...
660     * TODO of course this should be handled in a VMAILFS->open() .s_fd=-1,
661     * TODO but unfortunately it isn't yet */
662    if (i <= 0)
663       i = 0;
664    else {
665       if (sop->s_onclose != NULL)
666          (*sop->s_onclose)();
667       if (sop->s_wbuf != NULL)
668          n_free(sop->s_wbuf);
669 # ifdef mx_HAVE_XTLS
670       if (sop->s_use_tls) {
671          void *s_tls = sop->s_tls;
672 
673          sop->s_tls = NULL;
674          sop->s_use_tls = 0;
675          if(SSL_shutdown(s_tls) == 0) /* XXX proper error handling;signals! */
676             SSL_shutdown(s_tls);
677          SSL_free(s_tls);
678       }
679 # endif
680       i = close(i);
681    }
682    NYD_OU;
683    return i;
684 }
685 
686 enum okay
mx_socket_write(struct mx_socket * sop,char const * data)687 mx_socket_write(struct mx_socket *sop, char const *data) /* XXX INLINE */
688 {
689    enum okay rv;
690    NYD2_IN;
691 
692    rv = mx_socket_write1(sop, data, su_cs_len(data), 0);
693    NYD2_OU;
694    return rv;
695 }
696 
697 enum okay
mx_socket_write1(struct mx_socket * sop,char const * data,int size,int use_buffer)698 mx_socket_write1(struct mx_socket *sop, char const *data, int size,
699    int use_buffer)
700 {
701    enum okay rv = STOP;
702    int x;
703    NYD2_IN;
704 
705    if (use_buffer > 0) {
706       int di;
707 
708       if (sop->s_wbuf == NULL) {
709          sop->s_wbufsize = 4096;
710          sop->s_wbuf = n_alloc(sop->s_wbufsize);
711          sop->s_wbufpos = 0;
712       }
713       while (sop->s_wbufpos + size > sop->s_wbufsize) {
714          di = sop->s_wbufsize - sop->s_wbufpos;
715          size -= di;
716          if (sop->s_wbufpos > 0) {
717             su_mem_copy(&sop->s_wbuf[sop->s_wbufpos], data, di);
718             rv = mx_socket_write1(sop, sop->s_wbuf, sop->s_wbufsize, -1);
719          } else
720             rv = mx_socket_write1(sop, data, sop->s_wbufsize, -1);
721          if (rv != OKAY)
722             goto jleave;
723          data += di;
724          sop->s_wbufpos = 0;
725       }
726       if (size == sop->s_wbufsize) {
727          rv = mx_socket_write1(sop, data, sop->s_wbufsize, -1);
728          if (rv != OKAY)
729             goto jleave;
730       } else if (size > 0) {
731          su_mem_copy(&sop->s_wbuf[sop->s_wbufpos], data, size);
732          sop->s_wbufpos += size;
733       }
734       rv = OKAY;
735       goto jleave;
736    } else if (use_buffer == 0 && sop->s_wbuf != NULL && sop->s_wbufpos > 0) {
737       x = sop->s_wbufpos;
738       sop->s_wbufpos = 0;
739       if ((rv = mx_socket_write1(sop, sop->s_wbuf, x, -1)) != OKAY)
740          goto jleave;
741    }
742    if (size == 0) {
743       rv = OKAY;
744       goto jleave;
745    }
746 
747 # ifdef mx_HAVE_XTLS
748    if(sop->s_use_tls){
749       int errcnt, err;
750 
751       errcnt = 0;
752 jssl_retry:
753       x = SSL_write(sop->s_tls, data, size);
754       if(x < 0){
755          if(++errcnt < 3 && (err = su_err_no()) != su_ERR_WOULDBLOCK){
756             switch(SSL_get_error(sop->s_tls, x)){
757             case SSL_ERROR_WANT_READ:
758             case SSL_ERROR_WANT_WRITE:
759                n_err(_("TLS socket write error, retrying: %s\n"),
760                   su_err_doc(err));
761                goto jssl_retry;
762             }
763          }
764       }
765    } else
766 # endif /* mx_HAVE_XTLS */
767    {
768       x = a_netso_xwrite(sop->s_fd, data, size);
769    }
770    if (x != size) {
771       char o[512];
772 
773       snprintf(o, sizeof o, "%s write error",
774          (sop->s_desc ? sop->s_desc : "socket"));
775 # ifdef mx_HAVE_XTLS
776       if (sop->s_use_tls)
777          ssl_gen_err("%s", o);
778       else
779 # endif
780          n_perr(o, 0);
781       if (x < 0)
782          mx_socket_close(sop);
783       rv = STOP;
784       goto jleave;
785    }
786    rv = OKAY;
787 jleave:
788    NYD2_OU;
789    return rv;
790 }
791 
792 int
793 (mx_socket_getline)(char **line, uz *linesize, uz *linelen,
794    struct mx_socket *sop  su_DBG_LOC_ARGS_DECL)
795 {
796    int rv;
797    uz lsize;
798    char *lp_base, *lp;
799    NYD2_IN;
800 
801    lsize = *linesize;
802    lp_base = *line;
803    lp = lp_base;
804 
805    if (sop->s_rsz < 0) {
806       mx_socket_close(sop);
807       rv = sop->s_rsz;
808       goto jleave;
809    }
810 
811    do {
812       if (lp_base == NULL || PCMP(lp, >, lp_base + lsize - 128)) {
813          uz diff = P2UZ(lp - lp_base);
814          *linesize = (lsize += 256); /* XXX magic */
815          *line = lp_base = su_MEM_REALLOC_LOCOR(lp_base, lsize,
816                su_DBG_LOC_ARGS_ORUSE);
817          lp = lp_base + diff;
818       }
819 
820       if (sop->s_rbufptr == NULL ||
821             PCMP(sop->s_rbufptr, >=, sop->s_rbuf + sop->s_rsz)) {
822 # ifdef mx_HAVE_XTLS
823          if(sop->s_use_tls){
824             int errcnt, err;
825 
826             errcnt = 0;
827 jssl_retry:
828             sop->s_rsz = SSL_read(sop->s_tls, sop->s_rbuf, sizeof sop->s_rbuf);
829             if (sop->s_rsz <= 0) {
830                if (sop->s_rsz < 0) {
831                   char o[512];
832 
833                   if(++errcnt < 3 && (err = su_err_no()) != su_ERR_WOULDBLOCK){
834                      switch(SSL_get_error(sop->s_tls, sop->s_rsz)){
835                      case SSL_ERROR_WANT_READ:
836                      case SSL_ERROR_WANT_WRITE:
837                         n_err(_("TLS socket read error, retrying: %s\n"),
838                            su_err_doc(err));
839                         goto jssl_retry;
840                      }
841                   }
842                   snprintf(o, sizeof o, "%s",
843                      (sop->s_desc ?  sop->s_desc : "socket"));
844                   ssl_gen_err("%s", o);
845                }
846                break;
847             }
848          } else
849 # endif /* mx_HAVE_XTLS */
850          {
851 jagain:
852             sop->s_rsz = read(sop->s_fd, sop->s_rbuf, sizeof sop->s_rbuf);
853             if (sop->s_rsz <= 0) {
854                if (sop->s_rsz < 0) {
855                   char o[512];
856 
857                   if (su_err_no() == su_ERR_INTR)
858                      goto jagain;
859                   snprintf(o, sizeof o, "%s",
860                      (sop->s_desc ?  sop->s_desc : "socket"));
861                   n_perr(o, 0);
862                }
863                break;
864             }
865          }
866          sop->s_rbufptr = sop->s_rbuf;
867       }
868    } while ((*lp++ = *sop->s_rbufptr++) != '\n');
869    *lp = '\0';
870    lsize = P2UZ(lp - lp_base);
871 
872    if (linelen)
873       *linelen = lsize;
874    rv = (int)lsize;
875 jleave:
876    NYD2_OU;
877    return rv;
878 }
879 
880 #include "su/code-ou.h"
881 #endif /* mx_HAVE_NET */
882 /* s-it-mode */
883