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 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #ifndef HAVE_AMALGAMATION
41 #include <sys/socket.h>
45 #include <netinet/in.h>
47 #ifdef HAVE_ARPA_INET_H
48 # include <arpa/inet.h>
52 # include <openssl/err.h>
53 # include <openssl/rand.h>
54 # include <openssl/ssl.h>
55 # include <openssl/x509v3.h>
56 # include <openssl/x509.h>
59 /* Write to socket fd, restarting on EINTR, unless anything is written */
60 static long a_sock_xwrite(int fd
, char const *data
, size_t sz
);
63 a_sock_xwrite(int fd
, char const *data
, size_t sz
)
70 if ((wo
= write(fd
, data
+ wt
, sz
- wt
)) < 0) {
85 sclose(struct sock
*sp
)
92 /* TODO NOTE: we MUST NOT close the descriptor 0 here...
93 * TODO of course this should be handled in a VMAILFS->open() .s_fd=-1,
94 * TODO but unfortunately it isn't yet */
98 if (sp
->s_onclose
!= NULL
)
100 if (sp
->s_wbuf
!= NULL
)
104 void *s_ssl
= sp
->s_ssl
;
108 while (!SSL_shutdown(s_ssl
)) /* XXX proper error handling;signals! */
120 swrite(struct sock
*sp
, char const *data
)
125 rv
= swrite1(sp
, data
, strlen(data
), 0);
131 swrite1(struct sock
*sp
, char const *data
, int sz
, int use_buffer
)
137 if (use_buffer
> 0) {
140 if (sp
->s_wbuf
== NULL
) {
141 sp
->s_wbufsize
= 4096;
142 sp
->s_wbuf
= smalloc(sp
->s_wbufsize
);
145 while (sp
->s_wbufpos
+ sz
> sp
->s_wbufsize
) {
146 di
= sp
->s_wbufsize
- sp
->s_wbufpos
;
148 if (sp
->s_wbufpos
> 0) {
149 memcpy(sp
->s_wbuf
+ sp
->s_wbufpos
, data
, di
);
150 rv
= swrite1(sp
, sp
->s_wbuf
, sp
->s_wbufsize
, -1);
152 rv
= swrite1(sp
, data
, sp
->s_wbufsize
, -1);
158 if (sz
== sp
->s_wbufsize
) {
159 rv
= swrite1(sp
, data
, sp
->s_wbufsize
, -1);
163 memcpy(sp
->s_wbuf
+ sp
->s_wbufpos
, data
, sz
);
168 } else if (use_buffer
== 0 && sp
->s_wbuf
!= NULL
&& sp
->s_wbufpos
> 0) {
171 if ((rv
= swrite1(sp
, sp
->s_wbuf
, x
, -1)) != OKAY
)
182 x
= SSL_write(sp
->s_ssl
, data
, sz
);
184 switch (SSL_get_error(sp
->s_ssl
, x
)) {
185 case SSL_ERROR_WANT_READ
:
186 case SSL_ERROR_WANT_WRITE
:
193 x
= a_sock_xwrite(sp
->s_fd
, data
, sz
);
197 snprintf(o
, sizeof o
, "%s write error",
198 (sp
->s_desc
? sp
->s_desc
: "socket"));
201 ssl_gen_err("%s", o
);
216 static sigjmp_buf __sopen_actjmp
; /* TODO someday, we won't need it no more */
217 static int __sopen_sig
; /* TODO someday, we won't need it no more */
219 __sopen_onsig(int sig
) /* TODO someday, we won't need it no more */
221 NYD_X
; /* Signal handler */
222 if (__sopen_sig
< 0) {
223 /* Of course the following doesn't belong into a signal handler XXX */
226 if (__sopen_sig
== -1) {
228 _("\nInterrupting it could turn the (GNU/Linux+) DNS resolver "
230 " Wait until it's done, or do terminate the program\n"));
232 } else if ((i
= j
= ABS(__sopen_sig
)) + 15 < scrnwidth
) {
236 fputs("___( o)", stderr
);
237 putc((i
& 1) ? '=' : '>', stderr
);
245 siglongjmp(__sopen_actjmp
, 1);
250 sopen(struct sock
*sp
, struct url
*urlp
) /* TODO sighandling; refactor */
252 # ifdef HAVE_SO_SNDTIMEO
255 # ifdef HAVE_SO_LINGER
258 # ifdef HAVE_GETADDRINFO
259 char hbuf
[NI_MAXHOST
];
260 struct addrinfo hints
, *res0
= NULL
, *res
;
262 struct sockaddr_in servaddr
;
263 struct in_addr
**pptr
;
267 sighandler_type
volatile ohup
, oint
;
268 char const * volatile serv
;
269 int volatile sofd
= -1, errval
;
274 /* Connect timeouts after 30 seconds XXX configurable */
275 # ifdef HAVE_SO_SNDTIMEO
279 serv
= (urlp
->url_port
!= NULL
) ? urlp
->url_port
: urlp
->url_proto
;
281 if (options
& OPT_VERB
)
282 n_err(_("Resolving host \"%s:%s\" ... "),
283 urlp
->url_host
.s
, serv
);
285 /* Signal handling (in respect to __sopen_sig dealing) is heavy, but no
286 * healing until v15.0 and i want to end up with that functionality */
289 ohup
= safe_signal(SIGHUP
, &__sopen_onsig
);
290 oint
= safe_signal(SIGINT
, &__sopen_onsig
);
291 if (sigsetjmp(__sopen_actjmp
, 0)) {
294 (__sopen_sig
== SIGHUP
? _("Hangup") : _("Interrupted")));
303 # ifdef HAVE_GETADDRINFO
305 memset(&hints
, 0, sizeof hints
);
306 hints
.ai_socktype
= SOCK_STREAM
;
308 errval
= getaddrinfo(urlp
->url_host
.s
, serv
, &hints
, &res0
);
309 if (__sopen_sig
!= -1) {
310 __sopen_sig
= SIGINT
;
317 if (options
& OPT_VERB
)
318 n_err(_("failed\n"));
319 n_err(_("Lookup of \"%s:%s\" failed: %s\n"),
320 urlp
->url_host
.s
, serv
, gai_strerror(errval
));
322 /* Error seems to depend on how "smart" the /etc/service code is: is it
323 * "able" to state wether the service as such is NONAME or does it only
324 * check for the given ai_socktype.. */
325 if (errval
== EAI_NONAME
|| errval
== EAI_SERVICE
) {
326 if (serv
== urlp
->url_proto
&&
327 (serv
= url_servbyname(urlp
, NULL
)) != NULL
) {
328 n_err(_(" Trying standard protocol port \"%s\"\n"), serv
);
329 n_err(_(" If that succeeds consider including the "
330 "port in the URL!\n"));
333 if (serv
!= urlp
->url_port
)
334 n_err(_(" Including a port number in the URL may "
335 "circumvent this problem\n"));
341 if (options
& OPT_VERB
)
344 for (res
= res0
; res
!= NULL
&& sofd
< 0; res
= res
->ai_next
) {
345 if (options
& OPT_VERB
) {
346 if (getnameinfo(res
->ai_addr
, res
->ai_addrlen
, hbuf
, sizeof hbuf
,
347 NULL
, 0, NI_NUMERICHOST
))
348 memcpy(hbuf
, "unknown host", sizeof("unknown host"));
349 n_err(_("%sConnecting to \"%s:%s\" ..."),
350 (res
== res0
? "" : "\n"), hbuf
, serv
);
353 sofd
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
355 # ifdef HAVE_SO_SNDTIMEO
356 (void)setsockopt(sofd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
358 if (connect(sofd
, res
->ai_addr
, res
->ai_addrlen
)) {
372 # else /* HAVE_GETADDRINFO */
373 if (serv
== urlp
->url_proto
) {
374 if ((ep
= getservbyname(UNCONST(serv
), "tcp")) != NULL
)
375 urlp
->url_portno
= ntohs(ep
->s_port
);
377 if (options
& OPT_VERB
)
378 n_err(_("failed\n"));
379 if ((serv
= url_servbyname(urlp
, &urlp
->url_portno
)) != NULL
)
380 n_err(_(" Unknown service: \"%s\"\n"), urlp
->url_proto
);
381 n_err(_(" Trying standard protocol port \"%s\"\n"), serv
);
382 n_err(_(" If that succeeds consider including the "
383 "port in the URL!\n"));
385 n_err(_(" Unknown service: \"%s\"\n"), urlp
->url_proto
);
386 n_err(_(" Including a port number in the URL may "
387 "circumvent this problem\n"));
388 assert(sofd
== -1 && errval
== 0);
395 hp
= gethostbyname(urlp
->url_host
.s
);
396 if (__sopen_sig
!= -1) {
397 __sopen_sig
= SIGINT
;
405 if (options
& OPT_VERB
)
406 n_err(_("failed\n"));
408 case HOST_NOT_FOUND
: emsg
= N_("host not found"); break;
410 case TRY_AGAIN
: emsg
= N_("(maybe) try again later"); break;
411 case NO_RECOVERY
: emsg
= N_("non-recoverable server error"); break;
412 case NO_DATA
: emsg
= N_("valid name without IP address"); break;
414 n_err(_("Lookup of \"%s:%s\" failed: %s\n"),
415 urlp
->url_host
.s
, serv
, V_(emsg
));
417 } else if (options
& OPT_VERB
)
420 pptr
= (struct in_addr
**)hp
->h_addr_list
;
421 if ((sofd
= socket(AF_INET
, SOCK_STREAM
, 0)) == -1) {
422 n_perr(_("could not create socket"), 0);
423 assert(sofd
== -1 && errval
== 0);
427 memset(&servaddr
, 0, sizeof servaddr
);
428 servaddr
.sin_family
= AF_INET
;
429 servaddr
.sin_port
= htons(urlp
->url_portno
);
430 memcpy(&servaddr
.sin_addr
, *pptr
, sizeof(struct in_addr
));
431 if (options
& OPT_VERB
)
432 n_err(_("%sConnecting to \"%s:%d\" ... "),
433 "", inet_ntoa(**pptr
), (int)urlp
->url_portno
);
434 # ifdef HAVE_SO_SNDTIMEO
435 (void)setsockopt(sofd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
437 if (connect(sofd
, (struct sockaddr
*)&servaddr
, sizeof servaddr
)) {
443 # endif /* !HAVE_GETADDRINFO */
446 safe_signal(SIGINT
, oint
);
447 safe_signal(SIGHUP
, ohup
);
453 n_perr(_("Could not connect"), 0);
458 if (options
& OPT_VERB
)
459 n_err(_("connected.\n"));
461 /* And the regular timeouts XXX configurable */
462 # ifdef HAVE_SO_SNDTIMEO
465 (void)setsockopt(sofd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
466 (void)setsockopt(sofd
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof tv
);
468 # ifdef HAVE_SO_LINGER
471 (void)setsockopt(sofd
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof li
);
474 memset(sp
, 0, sizeof *sp
);
477 /* SSL/TLS upgrade? */
479 if (urlp
->url_needs_tls
) {
481 ohup
= safe_signal(SIGHUP
, &__sopen_onsig
);
482 oint
= safe_signal(SIGINT
, &__sopen_onsig
);
483 if (sigsetjmp(__sopen_actjmp
, 0)) {
484 n_err(_("%s during SSL/TLS handshake\n"),
485 (__sopen_sig
== SIGHUP
? _("Hangup") : _("Interrupted")));
490 if (ssl_open(urlp
, sp
) != OKAY
) {
497 safe_signal(SIGINT
, oint
);
498 safe_signal(SIGHUP
, ohup
);
501 # endif /* HAVE_SSL */
504 /* May need to bounce the signal to the lex.c trampoline (or wherever) */
505 if (__sopen_sig
!= 0) {
508 sigaddset(&cset
, __sopen_sig
);
509 sigprocmask(SIG_UNBLOCK
, &cset
, NULL
);
510 n_raise(__sopen_sig
);
517 (sgetline
)(char **line
, size_t *linesize
, size_t *linelen
, struct sock
*sp
536 if (lp_base
== NULL
|| PTRCMP(lp
, >, lp_base
+ lsize
- 128)) {
537 size_t diff
= PTR2SIZE(lp
- lp_base
);
538 *linesize
= (lsize
+= 256); /* XXX magic */
539 *line
= lp_base
= (srealloc
)(lp_base
, lsize SMALLOC_DEBUG_ARGSCALL
);
543 if (sp
->s_rbufptr
== NULL
||
544 PTRCMP(sp
->s_rbufptr
, >=, sp
->s_rbuf
+ sp
->s_rsz
)) {
548 sp
->s_rsz
= SSL_read(sp
->s_ssl
, sp
->s_rbuf
, sizeof sp
->s_rbuf
);
549 if (sp
->s_rsz
<= 0) {
552 switch(SSL_get_error(sp
->s_ssl
, sp
->s_rsz
)) {
553 case SSL_ERROR_WANT_READ
:
554 case SSL_ERROR_WANT_WRITE
:
557 snprintf(o
, sizeof o
, "%s",
558 (sp
->s_desc
? sp
->s_desc
: "socket"));
559 ssl_gen_err("%s", o
);
567 sp
->s_rsz
= read(sp
->s_fd
, sp
->s_rbuf
, sizeof sp
->s_rbuf
);
568 if (sp
->s_rsz
<= 0) {
573 snprintf(o
, sizeof o
, "%s",
574 (sp
->s_desc
? sp
->s_desc
: "socket"));
580 sp
->s_rbufptr
= sp
->s_rbuf
;
582 } while ((*lp
++ = *sp
->s_rbufptr
++) != '\n');
584 lsize
= PTR2SIZE(lp
- lp_base
);
593 #endif /* HAVE_SOCKETS */