X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=socket.c;h=3e4a3acd68390b721a72597e020704f709519ab7;hb=7f6138ffd4935043382ce5f867ee9e177e0a9787;hp=2ebdfc6a804d2e02d9858733db5fe7f8a7c46df8;hpb=8476bffcb54f81d028bcd86e2a9090161738a980;p=~andy%2Ffetchmail diff --git a/socket.c b/socket.c index 2ebdfc6a..3e4a3acd 100644 --- a/socket.c +++ b/socket.c @@ -179,7 +179,7 @@ static int handle_plugin(const char *host, (void) close(fds[1]); if ( (dup2(fds[0],0) == -1) || (dup2(fds[0],1) == -1) ) { report(stderr, GT_("dup2 failed\n")); - exit(1); + _exit(EXIT_FAILURE); } /* fds[0] is now connected to 0 and 1; close it */ (void) close(fds[0]); @@ -188,7 +188,7 @@ static int handle_plugin(const char *host, argvec = parse_plugin(plugin,host,service); execvp(*argvec, argvec); report(stderr, GT_("execvp(%s) failed\n"), *argvec); - exit(0); + _exit(EXIT_FAILURE); break; default: /* parent */ /* NOP */ @@ -200,29 +200,11 @@ static int handle_plugin(const char *host, } #endif /* HAVE_SOCKETPAIR */ -#ifdef __UNUSED__ - -int SockCheckOpen(int fd) -/* poll given socket; is it selectable? */ -{ - fd_set r, w, e; - int rt; - struct timeval tv; - - for (;;) - { - FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e); - FD_SET(fd, &e); - - tv.tv_sec = 0; tv.tv_usec = 0; - rt = select(fd+1, &r, &w, &e, &tv); - if (rt == -1 && (errno != EAGAIN && errno != EINTR)) - return 0; - if (rt != -1) - return 1; - } +/** Set socket to SO_KEEPALIVE. \return 0 for success. */ +int SockKeepalive(int sock) { + int keepalive = 1; + return setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof keepalive); } -#endif /* __UNUSED__ */ int UnixOpen(const char *path) { @@ -239,12 +221,12 @@ int UnixOpen(const char *path) return -1; } - /* Socket opened saved. Usefull if connect timeout - * because it can be closed. - */ - mailserver_socket_temp = sock; - - if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) < 0) + /* Socket opened saved. Usefull if connect timeout + * because it can be closed. + */ + mailserver_socket_temp = sock; + + if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) < 0) { int olderr = errno; fm_close(sock); /* don't use SockClose, no traffic yet */ @@ -252,9 +234,9 @@ int UnixOpen(const char *path) errno = olderr; sock = -1; } - - /* No connect timeout, then no need to set mailserver_socket_temp */ - mailserver_socket_temp = -1; + + /* No connect timeout, then no need to set mailserver_socket_temp */ + mailserver_socket_temp = -1; return sock; } @@ -264,6 +246,8 @@ int SockOpen(const char *host, const char *service, { struct addrinfo *ai, req; int i, acterr = 0; + int ord; + char errbuf[8192] = ""; #ifdef HAVE_SOCKETPAIR if (plugin) @@ -285,10 +269,13 @@ int SockOpen(const char *host, const char *service, return -1; } + /* NOTE a Linux bug here - getaddrinfo will happily return 127.0.0.1 + * twice if no IPv6 is configured */ i = -1; - for (ai = *ai0; ai; ai = ai->ai_next) { - char buf[80],pb[80]; - int gnie; + for (ord = 0, ai = *ai0; ai; ord++, ai = ai->ai_next) { + char buf[256]; /* hostname */ + char pb[256]; /* service name */ + int gnie; /* getnameinfo result code */ gnie = getnameinfo(ai->ai_addr, ai->ai_addrlen, buf, sizeof(buf), NULL, 0, NI_NUMERICHOST); if (gnie) @@ -299,17 +286,22 @@ int SockOpen(const char *host, const char *service, if (outlevel >= O_VERBOSE) report_build(stdout, GT_("Trying to connect to %s/%s..."), buf, pb); - i = socket(ai->ai_family, ai->ai_socktype, 0); + i = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (i < 0) { + int e = errno; /* mask EAFNOSUPPORT errors, they confuse users for * multihomed hosts */ if (errno != EAFNOSUPPORT) acterr = errno; if (outlevel >= O_VERBOSE) - report_complete(stdout, GT_("cannot create socket: %s\n"), strerror(errno)); + report_complete(stdout, GT_("cannot create socket: %s\n"), strerror(e)); + snprintf(errbuf+strlen(errbuf), sizeof(errbuf)-strlen(errbuf),\ + GT_("name %d: cannot create socket family %d type %d: %s\n"), ord, ai->ai_family, ai->ai_socktype, strerror(e)); continue; } + SockKeepalive(i); + /* Save socket descriptor. * Used to close the socket after connect timeout. */ mailserver_socket_temp = i; @@ -323,8 +315,9 @@ int SockOpen(const char *host, const char *service, if (outlevel >= O_VERBOSE) report_complete(stdout, GT_("connection failed.\n")); - if (outlevel > O_SILENT) + if (outlevel >= O_VERBOSE) report(stderr, GT_("connection to %s:%s [%s/%s] failed: %s.\n"), host, service, buf, pb, strerror(e)); + snprintf(errbuf+strlen(errbuf), sizeof(errbuf)-strlen(errbuf), GT_("name %d: connection to %s:%s [%s/%s] failed: %s.\n"), ord, host, service, buf, pb, strerror(e)); fm_close(i); i = -1; continue; @@ -342,8 +335,10 @@ int SockOpen(const char *host, const char *service, fm_freeaddrinfo(*ai0); *ai0 = NULL; - if (i == -1) + if (i == -1) { + report(stderr, GT_("Connection errors for this poll:\n%s"), errbuf); errno = acterr; + } return i; } @@ -441,7 +436,7 @@ int SockRead(int sock, char *buf, int len) /* OK... SSL_peek works a little different from MSG_PEEK Problem is that SSL_peek can return 0 if there is no data currently available. If, on the other - hand, we loose the socket, we also get a zero, but + hand, we lose the socket, we also get a zero, but the SSL_read then SEGFAULTS! To deal with this, we'll check the error code any time we get a return of zero from SSL_peek. If we have an error, we bail. @@ -582,7 +577,6 @@ SSL *SSLGetContext( int sock ) return _ssl_context[sock]; } - /* ok_return (preverify_ok) is 1 if this stage of certificate verification passed, or 0 if it failed. This callback lets us display informative errors, and perform additional validation (e.g. CN matches) */ @@ -608,7 +602,7 @@ static int SSL_verify_callback( int ok_return, X509_STORE_CTX *ctx, int strict ) if (outlevel >= O_VERBOSE) { if (depth == 0 && SSLverbose) - report(stderr, GT_("Server certificate:\n")); + report(stdout, GT_("Server certificate:\n")); else { if (_firstrun) { _firstrun = 0; @@ -666,10 +660,9 @@ static int SSL_verify_callback( int ok_return, X509_STORE_CTX *ctx, int strict ) if (_ssl_server_cname != NULL) { char *p1 = buf; char *p2 = _ssl_server_cname; - int n; int matched = 0; STACK_OF(GENERAL_NAME) *gens; - + /* RFC 2595 section 2.4: find a matching name * first find a match among alternative names */ gens = (STACK_OF(GENERAL_NAME) *)X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL); @@ -678,39 +671,27 @@ static int SSL_verify_callback( int ok_return, X509_STORE_CTX *ctx, int strict ) for (j = 0, r = sk_GENERAL_NAME_num(gens); j < r; ++j) { const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, j); if (gn->type == GEN_DNS) { - char *p1 = (char *)gn->d.ia5->data; - char *p2 = _ssl_server_cname; + char *pp1 = (char *)gn->d.ia5->data; + char *pp2 = _ssl_server_cname; if (outlevel >= O_VERBOSE) { - report(stdout, GT_("Subject Alternative Name: %s\n"), (tt = sdump(p1, (size_t)gn->d.ia5->length))); + report(stdout, GT_("Subject Alternative Name: %s\n"), (tt = sdump(pp1, (size_t)gn->d.ia5->length))); xfree(tt); } /* Name contains embedded NUL characters, so we complain. This * is likely a certificate spoofing attack. */ - if ((size_t)gn->d.ia5->length != strlen(p1)) { + if ((size_t)gn->d.ia5->length != strlen(pp1)) { report(stderr, GT_("Bad certificate: Subject Alternative Name contains NUL, aborting!\n")); sk_GENERAL_NAME_free(gens); return 0; } - if (*p1 == '*') { - ++p1; - n = strlen(p2) - strlen(p1); - if (n >= 0) - p2 += n; - } - if (0 == strcasecmp(p1, p2)) { - matched = 1; + if (name_match(pp1, pp2)) { + matched = 1; } } } - sk_GENERAL_NAME_free(gens); - } - if (*p1 == '*') { - ++p1; - n = strlen(p2) - strlen(p1); - if (n >= 0) - p2 += n; + GENERAL_NAMES_free(gens); } - if (0 == strcasecmp(p1, p2)) { + if (name_match(p1, p2)) { matched = 1; } if (!matched) { @@ -863,6 +844,7 @@ int SSLOpen(int sock, char *mycert, char *mykey, const char *myproto, int certck { struct stat randstat; int i; + long sslopts = SSL_OP_ALL; SSL_load_error_strings(); SSL_library_init(); @@ -893,7 +875,12 @@ int SSLOpen(int sock, char *mycert, char *mykey, const char *myproto, int certck _ssl_context[sock] = NULL; if(myproto) { if(!strcasecmp("ssl2",myproto)) { +#if HAVE_DECL_SSLV2_CLIENT_METHOD + 0 > 0 _ctx[sock] = SSL_CTX_new(SSLv2_client_method()); +#else + report(stderr, GT_("Your operating system does not support SSLv2.\n")); + return -1; +#endif } else if(!strcasecmp("ssl3",myproto)) { _ctx[sock] = SSL_CTX_new(SSLv3_client_method()); } else if(!strcasecmp("tls1",myproto)) { @@ -913,7 +900,13 @@ int SSLOpen(int sock, char *mycert, char *mykey, const char *myproto, int certck return(-1); } - SSL_CTX_set_options(_ctx[sock], SSL_OP_ALL); + { + char *tmp = getenv("FETCHMAIL_DISABLE_CBC_IV_COUNTERMEASURE"); + if (tmp == NULL || *tmp == '\0' || strspn(tmp, " \t") == strlen(tmp)) + sslopts &= ~ SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + } + + SSL_CTX_set_options(_ctx[sock], sslopts); if (certck) { SSL_CTX_set_verify(_ctx[sock], SSL_VERIFY_PEER, SSL_ck_verify_callback); @@ -1032,30 +1025,6 @@ int SockClose(int sock) } #endif -#ifdef __UNUSED__ - /* - * This hangs in RedHat 6.2 after fetchmail runs for a while a - * FIN_WAIT2 comes up in netstat and fetchmail never returns from - * the recv system call. (Reported from jtnews - * , Wed, 24 May 2000 21:26:02.) - * - * Half-close the connection first so the other end gets notified. - * - * This stops sends but allows receives (effectively, it sends a - * TCP ). */ - if (shutdown(sock, 1) == 0) { - char ch; - /* If there is any data still waiting in the queue, discard it. - * Call recv() until either it returns 0 (meaning we received a FIN) - * or any error occurs. This makes sure all data sent by the other - * side is acknowledged at the TCP level. - */ - if (fm_peek(sock, &ch, 1) > 0) - while (fm_read(sock, &ch, 1) > 0) - continue; - } -#endif /* __UNUSED__ */ - /* if there's an error closing at this point, not much we can do */ return(fm_close(sock)); /* this is guarded */ } @@ -1068,7 +1037,7 @@ int SockClose(int sock) */ static ssize_t cygwin_read(int sock, void *buf, size_t count) { - char *bp = buf; + char *bp = (char *)buf; size_t n = 0; if ((n = read(sock, bp, count)) == (size_t)-1) @@ -1088,21 +1057,3 @@ static ssize_t cygwin_read(int sock, void *buf, size_t count) return count; } #endif /* __CYGWIN__ */ - -#ifdef MAIN -/* - * Use the chargen service to test input buffering directly. - * You may have to uncomment the `chargen' service description in your - * inetd.conf (and then SIGHUP inetd) for this to work. */ -main() -{ - int sock = SockOpen("localhost", "chargen", NULL); - char buf[80]; - - while (SockRead(sock, buf, sizeof(buf)-1)) - SockWrite(1, buf, strlen(buf)); - SockClose(sock); -} -#endif /* MAIN */ - -/* socket.c ends here */