1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
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
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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
36 #ifndef HAVE_AMALGAMATION
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>*/
54 #include <sys/socket.h>
58 #include <netinet/in.h>
60 #ifdef HAVE_ARPA_INET_H
61 # include <arpa/inet.h>
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>
73 static bool_t
a_socket_open(struct sock
*sp
, struct url
*urlp
);
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 */
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"));
93 siglongjmp(__sopen_actjmp
, 1);
98 a_socket_open(struct sock
*sp
, struct url
*urlp
) /* TODO sigstuff; refactor */
100 # ifdef HAVE_SO_XTIMEO
103 # ifdef HAVE_SO_LINGER
106 # ifdef HAVE_GETADDRINFO
108 # define NI_MAXHOST 1025
110 char hbuf
[NI_MAXHOST
];
111 struct addrinfo hints
, *res0
= NULL
, *res
;
113 struct sockaddr_in servaddr
;
114 struct in_addr
**pptr
;
118 sighandler_type
volatile ohup
, oint
;
119 char const * volatile serv
;
120 int volatile sofd
= -1, errval
;
123 memset(sp
, 0, sizeof *sp
);
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 */
135 ohup
= safe_signal(SIGHUP
, &__sopen_onsig
);
136 oint
= safe_signal(SIGINT
, &__sopen_onsig
);
137 if (sigsetjmp(__sopen_actjmp
, 0)) {
140 (__sopen_sig
== SIGHUP
? _("Hangup") : _("Interrupted")));
149 # ifdef HAVE_GETADDRINFO
151 memset(&hints
, 0, sizeof hints
);
152 hints
.ai_socktype
= SOCK_STREAM
;
154 errval
= getaddrinfo(urlp
->url_host
.s
, serv
, &hints
, &res0
);
155 if (__sopen_sig
!= -1) {
156 __sopen_sig
= SIGINT
;
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
&&
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"));
180 if (serv
!= urlp
->url_port
)
181 n_err(_(" Including a port number in the URL may "
182 "circumvent this problem\n"));
188 if (n_poption
& n_PO_D_V
)
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
);
202 (errval
= a_socket_connect(sofd
, res
->ai_addr
, res
->ai_addrlen
)
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
);
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"));
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);
236 hp
= gethostbyname(urlp
->url_host
.s
);
237 if (__sopen_sig
!= -1) {
238 __sopen_sig
= SIGINT
;
246 if (n_poption
& n_PO_D_V
)
247 n_err(_("failed\n"));
249 case HOST_NOT_FOUND
: emsg
= N_("host not found"); break;
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
));
258 } else if (n_poption
& n_PO_D_V
)
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);
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
)
279 # endif /* !HAVE_GETADDRINFO */
282 safe_signal(SIGINT
, oint
);
283 safe_signal(SIGHUP
, ohup
);
288 n_perr(_("Could not connect"), errval
);
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
302 (void)setsockopt(sofd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
303 (void)setsockopt(sofd
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof tv
);
305 # ifdef HAVE_SO_LINGER
308 (void)setsockopt(sofd
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof li
);
311 /* SSL/TLS upgrade? */
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
;
322 if(getaddrinfo(urlp
->url_host
.s
, NULL
, &hints
, &res0
) == 0)
325 urlp
->url_flags
|= n_URL_HOST_IS_NAME
;
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")));
339 if(!n_tls_open(urlp
, sp
)){
343 }else if(urlp
->url_cproto
== CPROTO_CERTINFO
)
347 safe_signal(SIGINT
, oint
);
348 safe_signal(SIGHUP
, ohup
);
352 # endif /* HAVE_TLS */
355 /* May need to bounce the signal to the go.c trampoline (or wherever) */
356 if (__sopen_sig
!= 0) {
359 sigaddset(&cset
, __sopen_sig
);
360 sigprocmask(SIG_UNBLOCK
, &cset
, NULL
);
361 n_raise(__sopen_sig
);
368 a_socket_connect(int fd
, struct sockaddr
*soap
, size_t soapl
){
372 #ifdef HAVE_NONBLOCKSOCK
373 rv
= fcntl(fd
, F_GETFL
, 0);
374 if(rv
!= -1 && !fcntl(fd
, F_SETFL
, rv
| O_NONBLOCK
)){
376 struct timeval tv
; /* XXX configurable */
380 if(connect(fd
, soap
, soapl
) && (i
= n_err_no
) != n_ERR_INPROGRESS
){
387 if(n_poption
& n_PO_D_V
){
396 if((soe
= select(fd
+ 1, NULL
, &fdset
, NULL
, &tv
)) == 1){
399 getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &rv
, &sol
);
400 fcntl(fd
, F_SETFL
, i
);
401 if(n_poption
& n_PO_D_V
)
404 if((n_poption
& n_PO_D_V
) && --i
> 0){
410 n_err(_(" timeout\n"));
416 #endif /* HAVE_NONBLOCKSOCK */
418 if(!connect(fd
, soap
, soapl
))
421 #ifdef HAVE_NONBLOCKSOCK
425 #ifdef HAVE_NONBLOCKSOCK
428 n_perr(_("connect(2) failed:"), rv
);
436 a_socket_xwrite(int fd
, char const *data
, size_t sz
)
443 if ((wo
= write(fd
, data
+ wt
, sz
- wt
)) < 0) {
444 if (n_err_no
== n_ERR_INTR
)
458 sclose(struct sock
*sp
)
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 */
471 if (sp
->s_onclose
!= NULL
)
473 if (sp
->s_wbuf
!= NULL
)
477 void *s_tls
= sp
->s_tls
;
481 while (!SSL_shutdown(s_tls
)) /* XXX proper error handling;signals! */
493 swrite(struct sock
*sp
, char const *data
)
498 rv
= swrite1(sp
, data
, strlen(data
), 0);
504 swrite1(struct sock
*sp
, char const *data
, int sz
, int use_buffer
)
510 if (use_buffer
> 0) {
513 if (sp
->s_wbuf
== NULL
) {
514 sp
->s_wbufsize
= 4096;
515 sp
->s_wbuf
= n_alloc(sp
->s_wbufsize
);
518 while (sp
->s_wbufpos
+ sz
> sp
->s_wbufsize
) {
519 di
= sp
->s_wbufsize
- sp
->s_wbufpos
;
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);
525 rv
= swrite1(sp
, data
, sp
->s_wbufsize
, -1);
531 if (sz
== sp
->s_wbufsize
) {
532 rv
= swrite1(sp
, data
, sp
->s_wbufsize
, -1);
536 memcpy(sp
->s_wbuf
+ sp
->s_wbufpos
, data
, sz
);
541 } else if (use_buffer
== 0 && sp
->s_wbuf
!= NULL
&& sp
->s_wbufpos
> 0) {
544 if ((rv
= swrite1(sp
, sp
->s_wbuf
, x
, -1)) != OKAY
)
555 x
= SSL_write(sp
->s_tls
, data
, sz
);
557 switch (SSL_get_error(sp
->s_tls
, x
)) {
558 case SSL_ERROR_WANT_READ
:
559 case SSL_ERROR_WANT_WRITE
:
566 x
= a_socket_xwrite(sp
->s_fd
, data
, sz
);
571 snprintf(o
, sizeof o
, "%s write error",
572 (sp
->s_desc
? sp
->s_desc
: "socket"));
575 ssl_gen_err("%s", o
);
591 sopen(struct sock
*sp
, struct url
*urlp
){
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
);
602 ui8_t pbuf
[4 + 1 + 255 + 2];
607 if(!url_parse(&url2
, CPROTO_SOCKS
, cp
)){
608 n_err(_("Failed to parse *socks-proxy*: %s\n"), cp
);
611 if(urlp
->url_host
.l
> 255){
612 n_err(_("*socks-proxy*: hostname too long: %s\n"),
617 if (n_poption
& n_PO_D_V
)
618 n_err(_("Connecting via *socks-proxy* to %s:%s ...\n"),
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
);
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){
633 n_perr("*socks-proxy*", 0);
639 /* Receive greeting */
640 if(read(sp
->s_fd
, pbuf
, 2) != 2)
642 if(pbuf
[0] != 0x05 || pbuf
[1] != 0x00){
644 emsg
= N_("unexpected reply\n");
646 /* I18N: error message and failing URL */
647 n_err(_("*socks-proxy*: %s: %s\n"), V_(emsg
), cp
);
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
);
661 x
= htons(urlp
->url_portno
);
662 memcpy(&pbuf
[i
+= urlp
->url_host
.l
], (ui8_t
*)&x
, sizeof x
);
665 if(write(sp
->s_fd
, pbuf
, i
) != (ssize_t
)i
)
669 if((i
= read(sp
->s_fd
, pbuf
, 4)) != 4)
671 /* Version 5, reserved must be 0 */
672 if(pbuf
[0] != 0x05 || pbuf
[2] != 0x00)
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;
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 */
694 case 0x01: i
= 4; break;
695 case 0x03: i
= 1; break;
696 case 0x04: i
= 16; break;
697 default: goto jesocksreply
;
700 if(read(sp
->s_fd
, pbuf
, i
) != (ssize_t
)i
)
702 if(i
== 1 + sizeof(ui16_t
)){
704 if(read(sp
->s_fd
, pbuf
, i
) != (ssize_t
)i
)
715 (sgetline
)(char **line
, size_t *linesize
, size_t *linelen
, struct sock
*sp
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
);
741 if (sp
->s_rbufptr
== NULL
||
742 PTRCMP(sp
->s_rbufptr
, >=, sp
->s_rbuf
+ sp
->s_rsz
)) {
746 sp
->s_rsz
= SSL_read(sp
->s_tls
, sp
->s_rbuf
, sizeof sp
->s_rbuf
);
747 if (sp
->s_rsz
<= 0) {
751 switch(SSL_get_error(sp
->s_tls
, sp
->s_rsz
)) {
752 case SSL_ERROR_WANT_READ
:
753 case SSL_ERROR_WANT_WRITE
:
756 snprintf(o
, sizeof o
, "%s",
757 (sp
->s_desc
? sp
->s_desc
: "socket"));
758 ssl_gen_err("%s", o
);
766 sp
->s_rsz
= read(sp
->s_fd
, sp
->s_rbuf
, sizeof sp
->s_rbuf
);
767 if (sp
->s_rsz
<= 0) {
771 if (n_err_no
== n_ERR_INTR
)
773 snprintf(o
, sizeof o
, "%s",
774 (sp
->s_desc
? sp
->s_desc
: "socket"));
780 sp
->s_rbufptr
= sp
->s_rbuf
;
782 } while ((*lp
++ = *sp
->s_rbufptr
++) != '\n');
784 lsize
= PTR2SIZE(lp
- lp_base
);
793 #endif /* HAVE_SOCKETS */