mk-conf.sh, mk-mk.in: diversify..
[s-mailx.git] / socket.c
blobbf691d0641e50141248646c823143f85862a2038
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 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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
30 * SUCH DAMAGE.
32 #undef n_FILE
33 #define n_FILE socket
35 #ifndef HAVE_AMALGAMATION
36 # include "nail.h"
37 #endif
39 EMPTY_FILE()
40 #ifdef HAVE_SOCKETS
41 #include <sys/socket.h>
43 #include <netdb.h>
45 #include <netinet/in.h>
47 #ifdef HAVE_ARPA_INET_H
48 # include <arpa/inet.h>
49 #endif
51 #ifdef HAVE_OPENSSL
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>
57 #endif
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);
62 static long
63 a_sock_xwrite(int fd, char const *data, size_t sz)
65 long rv = -1, wo;
66 size_t wt = 0;
67 NYD_ENTER;
69 do {
70 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
71 if (errno == EINTR)
72 continue;
73 else
74 goto jleave;
76 wt += wo;
77 } while (wt < sz);
78 rv = (long)sz;
79 jleave:
80 NYD_LEAVE;
81 return rv;
84 FL int
85 sclose(struct sock *sp)
87 int i;
88 NYD_ENTER;
90 i = sp->s_fd;
91 sp->s_fd = -1;
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 */
95 if (i <= 0)
96 i = 0;
97 else {
98 if (sp->s_onclose != NULL)
99 (*sp->s_onclose)();
100 if (sp->s_wbuf != NULL)
101 free(sp->s_wbuf);
102 # ifdef HAVE_OPENSSL
103 if (sp->s_use_ssl) {
104 void *s_ssl = sp->s_ssl;
106 sp->s_ssl = NULL;
107 sp->s_use_ssl = 0;
108 while (!SSL_shutdown(s_ssl)) /* XXX proper error handling;signals! */
110 SSL_free(s_ssl);
112 # endif
113 i = close(i);
115 NYD_LEAVE;
116 return i;
119 FL enum okay
120 swrite(struct sock *sp, char const *data)
122 enum okay rv;
123 NYD2_ENTER;
125 rv = swrite1(sp, data, strlen(data), 0);
126 NYD2_LEAVE;
127 return rv;
130 FL enum okay
131 swrite1(struct sock *sp, char const *data, int sz, int use_buffer)
133 enum okay rv = STOP;
134 int x;
135 NYD2_ENTER;
137 if (use_buffer > 0) {
138 int di;
140 if (sp->s_wbuf == NULL) {
141 sp->s_wbufsize = 4096;
142 sp->s_wbuf = smalloc(sp->s_wbufsize);
143 sp->s_wbufpos = 0;
145 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
146 di = sp->s_wbufsize - sp->s_wbufpos;
147 sz -= di;
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);
151 } else
152 rv = swrite1(sp, data, sp->s_wbufsize, -1);
153 if (rv != OKAY)
154 goto jleave;
155 data += di;
156 sp->s_wbufpos = 0;
158 if (sz == sp->s_wbufsize) {
159 rv = swrite1(sp, data, sp->s_wbufsize, -1);
160 if (rv != OKAY)
161 goto jleave;
162 } else if (sz) {
163 memcpy(sp->s_wbuf+ sp->s_wbufpos, data, sz);
164 sp->s_wbufpos += sz;
166 rv = OKAY;
167 goto jleave;
168 } else if (use_buffer == 0 && sp->s_wbuf != NULL && sp->s_wbufpos > 0) {
169 x = sp->s_wbufpos;
170 sp->s_wbufpos = 0;
171 if ((rv = swrite1(sp, sp->s_wbuf, x, -1)) != OKAY)
172 goto jleave;
174 if (sz == 0) {
175 rv = OKAY;
176 goto jleave;
179 # ifdef HAVE_OPENSSL
180 if (sp->s_use_ssl) {
181 jssl_retry:
182 x = SSL_write(sp->s_ssl, data, sz);
183 if (x < 0) {
184 switch (SSL_get_error(sp->s_ssl, x)) {
185 case SSL_ERROR_WANT_READ:
186 case SSL_ERROR_WANT_WRITE:
187 goto jssl_retry;
190 } else
191 # endif
193 x = a_sock_xwrite(sp->s_fd, data, sz);
195 if (x != sz) {
196 char o[512];
197 snprintf(o, sizeof o, "%s write error",
198 (sp->s_desc ? sp->s_desc : "socket"));
199 # ifdef HAVE_OPENSSL
200 if (sp->s_use_ssl)
201 ssl_gen_err("%s", o);
202 else
203 # endif
204 n_perr(o, 0);
205 if (x < 0)
206 sclose(sp);
207 rv = STOP;
208 goto jleave;
210 rv = OKAY;
211 jleave:
212 NYD2_LEAVE;
213 return rv;
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 */
218 static void
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 */
224 int i, j;
226 if (__sopen_sig == -1) {
227 fprintf(stderr,
228 _("\nInterrupting it could turn the (GNU/Linux+) DNS resolver "
229 "unusable.\n"
230 " Wait until it's done, or do terminate the program\n"));
231 __sopen_sig = -2;
232 } else if ((i = j = ABS(__sopen_sig)) + 15 < scrnwidth) {
233 putc('\r', stderr);
234 for (; j > 0; --j)
235 putc(' ', stderr);
236 fputs("___( o)", stderr);
237 putc((i & 1) ? '=' : '>', stderr);
238 putc(' ', stderr);
239 putc(' ', stderr);
240 ++i;
241 __sopen_sig = -i;
243 } else {
244 __sopen_sig = sig;
245 siglongjmp(__sopen_actjmp, 1);
249 FL bool_t
250 sopen(struct sock *sp, struct url *urlp) /* TODO sighandling; refactor */
252 # ifdef HAVE_SO_SNDTIMEO
253 struct timeval tv;
254 # endif
255 # ifdef HAVE_SO_LINGER
256 struct linger li;
257 # endif
258 # ifdef HAVE_GETADDRINFO
259 char hbuf[NI_MAXHOST];
260 struct addrinfo hints, *res0 = NULL, *res;
261 # else
262 struct sockaddr_in servaddr;
263 struct in_addr **pptr;
264 struct hostent *hp;
265 struct servent *ep;
266 # endif
267 sighandler_type volatile ohup, oint;
268 char const * volatile serv;
269 int volatile sofd = -1, errval;
270 NYD_ENTER;
272 UNINIT(errval, 0);
274 /* Connect timeouts after 30 seconds XXX configurable */
275 # ifdef HAVE_SO_SNDTIMEO
276 tv.tv_sec = 30;
277 tv.tv_usec = 0;
278 # endif
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 */
287 hold_sigs();
288 __sopen_sig = 0;
289 ohup = safe_signal(SIGHUP, &__sopen_onsig);
290 oint = safe_signal(SIGINT, &__sopen_onsig);
291 if (sigsetjmp(__sopen_actjmp, 0)) {
292 jpseudo_jump:
293 n_err("%s\n",
294 (__sopen_sig == SIGHUP ? _("Hangup") : _("Interrupted")));
295 if (sofd >= 0) {
296 close(sofd);
297 sofd = -1;
299 goto jjumped;
301 rele_sigs();
303 # ifdef HAVE_GETADDRINFO
304 for (;;) {
305 memset(&hints, 0, sizeof hints);
306 hints.ai_socktype = SOCK_STREAM;
307 __sopen_sig = -1;
308 errval = getaddrinfo(urlp->url_host.s, serv, &hints, &res0);
309 if (__sopen_sig != -1) {
310 __sopen_sig = SIGINT;
311 goto jpseudo_jump;
313 __sopen_sig = 0;
314 if (errval == 0)
315 break;
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"));
331 continue;
333 if (serv != urlp->url_port)
334 n_err(_(" Including a port number in the URL may "
335 "circumvent this problem\n"));
337 assert(sofd == -1);
338 errval = 0;
339 goto jjumped;
341 if (options & OPT_VERB)
342 n_err(_("done\n"));
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);
354 if (sofd >= 0) {
355 # ifdef HAVE_SO_SNDTIMEO
356 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
357 # endif
358 if (connect(sofd, res->ai_addr, res->ai_addrlen)) {
359 errval = errno;
360 close(sofd);
361 sofd = -1;
366 jjumped:
367 if (res0 != NULL) {
368 freeaddrinfo(res0);
369 res0 = NULL;
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);
376 else {
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"));
384 else {
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);
389 goto jjumped;
394 __sopen_sig = -1;
395 hp = gethostbyname(urlp->url_host.s);
396 if (__sopen_sig != -1) {
397 __sopen_sig = SIGINT;
398 goto jpseudo_jump;
400 __sopen_sig = 0;
402 if (hp == NULL) {
403 char const *emsg;
405 if (options & OPT_VERB)
406 n_err(_("failed\n"));
407 switch (h_errno) {
408 case HOST_NOT_FOUND: emsg = N_("host not found"); break;
409 default:
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));
416 goto jjumped;
417 } else if (options & OPT_VERB)
418 n_err(_("done\n"));
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);
424 goto jjumped;
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);
436 # endif
437 if (connect(sofd, (struct sockaddr*)&servaddr, sizeof servaddr)) {
438 errval = errno;
439 close(sofd);
440 sofd = -1;
442 jjumped:
443 # endif /* !HAVE_GETADDRINFO */
445 hold_sigs();
446 safe_signal(SIGINT, oint);
447 safe_signal(SIGHUP, ohup);
448 rele_sigs();
450 if (sofd < 0) {
451 if (errval != 0) {
452 errno = errval;
453 n_perr(_("Could not connect"), 0);
455 goto jleave;
458 if (options & OPT_VERB)
459 n_err(_("connected.\n"));
461 /* And the regular timeouts XXX configurable */
462 # ifdef HAVE_SO_SNDTIMEO
463 tv.tv_sec = 42;
464 tv.tv_usec = 0;
465 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
466 (void)setsockopt(sofd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
467 # endif
468 # ifdef HAVE_SO_LINGER
469 li.l_onoff = 1;
470 li.l_linger = 42;
471 (void)setsockopt(sofd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
472 # endif
474 memset(sp, 0, sizeof *sp);
475 sp->s_fd = sofd;
477 /* SSL/TLS upgrade? */
478 # ifdef HAVE_SSL
479 if (urlp->url_needs_tls) {
480 hold_sigs();
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")));
486 goto jsclose;
488 rele_sigs();
490 if (ssl_open(urlp, sp) != OKAY) {
491 jsclose:
492 sclose(sp);
493 sofd = -1;
496 hold_sigs();
497 safe_signal(SIGINT, oint);
498 safe_signal(SIGHUP, ohup);
499 rele_sigs();
501 # endif /* HAVE_SSL */
503 jleave:
504 /* May need to bounce the signal to the lex.c trampoline (or wherever) */
505 if (__sopen_sig != 0) {
506 sigset_t cset;
507 sigemptyset(&cset);
508 sigaddset(&cset, __sopen_sig);
509 sigprocmask(SIG_UNBLOCK, &cset, NULL);
510 n_raise(__sopen_sig);
512 NYD_LEAVE;
513 return (sofd >= 0);
516 FL int
517 (sgetline)(char **line, size_t *linesize, size_t *linelen, struct sock *sp
518 SMALLOC_DEBUG_ARGS)
520 int rv;
521 size_t lsize;
522 char *lp_base, *lp;
523 NYD2_ENTER;
525 lsize = *linesize;
526 lp_base = *line;
527 lp = lp_base;
529 if (sp->s_rsz < 0) {
530 sclose(sp);
531 rv = sp->s_rsz;
532 goto jleave;
535 do {
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);
540 lp = lp_base + diff;
543 if (sp->s_rbufptr == NULL ||
544 PTRCMP(sp->s_rbufptr, >=, sp->s_rbuf + sp->s_rsz)) {
545 # ifdef HAVE_OPENSSL
546 if (sp->s_use_ssl) {
547 jssl_retry:
548 sp->s_rsz = SSL_read(sp->s_ssl, sp->s_rbuf, sizeof sp->s_rbuf);
549 if (sp->s_rsz <= 0) {
550 if (sp->s_rsz < 0) {
551 char o[512];
552 switch(SSL_get_error(sp->s_ssl, sp->s_rsz)) {
553 case SSL_ERROR_WANT_READ:
554 case SSL_ERROR_WANT_WRITE:
555 goto jssl_retry;
557 snprintf(o, sizeof o, "%s",
558 (sp->s_desc ? sp->s_desc : "socket"));
559 ssl_gen_err("%s", o);
561 break;
563 } else
564 # endif
566 jagain:
567 sp->s_rsz = read(sp->s_fd, sp->s_rbuf, sizeof sp->s_rbuf);
568 if (sp->s_rsz <= 0) {
569 if (sp->s_rsz < 0) {
570 char o[512];
571 if (errno == EINTR)
572 goto jagain;
573 snprintf(o, sizeof o, "%s",
574 (sp->s_desc ? sp->s_desc : "socket"));
575 n_perr(o, 0);
577 break;
580 sp->s_rbufptr = sp->s_rbuf;
582 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
583 *lp = '\0';
584 lsize = PTR2SIZE(lp - lp_base);
586 if (linelen)
587 *linelen = lsize;
588 rv = (int)lsize;
589 jleave:
590 NYD2_LEAVE;
591 return rv;
593 #endif /* HAVE_SOCKETS */
595 /* s-it-mode */