X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fip.c;h=04b86060fa42cd23e31ed36b0641fb71e509cad3;hb=3cc3f7625d68c169d47dadbefa74233e5fe7ed55;hp=91b74e20ed0b95898152295efcc6fcf1ba14af4c;hpb=0f0c8159c43045f4ad847a0129dca7eddd313285;p=exim.git diff --git a/src/src/ip.c b/src/src/ip.c index 91b74e20e..04b86060f 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for doing things with sockets. With the advent of IPv6 this has @@ -97,24 +97,11 @@ ip_addrinfo(const uschar *address, struct sockaddr_in6 *saddr) * Bind socket to interface and port * *************************************************/ -/* This function binds a socket to a local interface address and port. For a -wildcard IPv6 bind, the address is ":". - -Arguments: - sock the socket - af AF_INET or AF_INET6 - the socket type - address the IP address, in text form - port the IP port (host order) - -Returns: the result of bind() -*/ - int -ip_bind(int sock, int af, uschar *address, int port) +ip_addr(void * sin_, int af, const uschar * address, int port) { -int s_len; -union sockaddr_46 sin; -memset(&sin, 0, sizeof(sin)); +union sockaddr_46 * sin = sin_; +memset(sin, 0, sizeof(*sin)); /* Setup code when using an IPv6 socket. The wildcard address is ":", to ensure an IPv6 socket is used. */ @@ -124,15 +111,13 @@ if (af == AF_INET6) { if (address[0] == ':' && address[1] == 0) { - sin.v6.sin6_family = AF_INET6; - sin.v6.sin6_addr = in6addr_any; + sin->v6.sin6_family = AF_INET6; + sin->v6.sin6_addr = in6addr_any; } else - { - ip_addrinfo(address, &sin.v6); /* Panic-dies on error */ - } - sin.v6.sin6_port = htons(port); - s_len = sizeof(sin.v6); + ip_addrinfo(address, &sin->v6); /* Panic-dies on error */ + sin->v6.sin6_port = htons(port); + return sizeof(sin->v6); } else #else /* HAVE_IPv6 */ @@ -142,17 +127,34 @@ af = af; /* Avoid compiler warning */ /* Setup code when using IPv4 socket. The wildcard address is "". */ { - sin.v4.sin_family = AF_INET; - sin.v4.sin_port = htons(port); - s_len = sizeof(sin.v4); - if (address[0] == 0) - sin.v4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY; - else - sin.v4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(CS address); + sin->v4.sin_family = AF_INET; + sin->v4.sin_port = htons(port); + sin->v4.sin_addr.s_addr = address[0] == 0 + ? (S_ADDR_TYPE)INADDR_ANY + : (S_ADDR_TYPE)inet_addr(CS address); + return sizeof(sin->v4); } +} + -/* Now we can call the bind() function */ +/* This function binds a socket to a local interface address and port. For a +wildcard IPv6 bind, the address is ":". + +Arguments: + sock the socket + af AF_INET or AF_INET6 - the socket type + address the IP address, in text form + port the IP port (host order) + +Returns: the result of bind() +*/ + +int +ip_bind(int sock, int af, uschar *address, int port) +{ +union sockaddr_46 sin; +int s_len = ip_addr(&sin, af, address, port); return bind(sock, (struct sockaddr *)&sin, s_len); } @@ -216,6 +218,7 @@ IPv6 support. */ /* If no connection timeout is set, just call connect() without setting a timer, thereby allowing the inbuilt OS timeout to operate. */ +callout_address = string_sprintf("[%s]:%d", address, port); sigalrm_seen = FALSE; if (timeout > 0) alarm(timeout); rc = connect(sock, s_ptr, s_len); @@ -226,24 +229,22 @@ alarm(0); can't think of any other way of doing this. It converts a connection refused into a timeout if the timeout is set to 999999. */ -if (running_in_test_harness) +if (running_in_test_harness && save_errno == ECONNREFUSED && timeout == 999999) { - if (save_errno == ECONNREFUSED && timeout == 999999) - { - rc = -1; - save_errno = EINTR; - sigalrm_seen = TRUE; - } + rc = -1; + save_errno = EINTR; + sigalrm_seen = TRUE; } /* Success */ -if (rc >= 0) return 0; +if (rc >= 0) + return 0; /* A failure whose error code is "Interrupted system call" is in fact an externally applied timeout if the signal handler has been run. */ -errno = (save_errno == EINTR && sigalrm_seen)? ETIMEDOUT : save_errno; +errno = save_errno == EINTR && sigalrm_seen ? ETIMEDOUT : save_errno; return -1; } @@ -289,9 +290,7 @@ namelen = Ustrlen(hostname); if (hostname[0] == '[' && hostname[namelen - 1] == ']') { - uschar * host = string_copy(hostname); - host[namelen - 1] = 0; - host++; + uschar * host = string_copyn(hostname+1, namelen-2); if (string_is_ip_address(host, NULL) == 0) { *errstr = string_sprintf("malformed IP address \"%s\"", hostname); @@ -303,15 +302,15 @@ if (hostname[0] == '[' && /* Otherwise check for an unadorned IP address */ else if (string_is_ip_address(hostname, NULL) != 0) - shost.name = shost.address = string_copy(hostname); + shost.name = shost.address = string_copyn(hostname, namelen); /* Otherwise lookup IP address(es) from the name */ else { - shost.name = string_copy(hostname); - if (host_find_byname(&shost, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, - FALSE) != HOST_FOUND) + shost.name = string_copyn(hostname, namelen); + if (host_find_byname(&shost, NULL, HOST_FIND_QUALIFY_SINGLE, + NULL, FALSE) != HOST_FOUND) { *errstr = string_sprintf("no IP address found for host %s", shost.name); return -1; @@ -322,9 +321,9 @@ else for (h = &shost; h != NULL; h = h->next) { - fd = (Ustrchr(h->address, ':') != 0) - ? (fd6 < 0) ? (fd6 = ip_socket(type, af = AF_INET6)) : fd6 - : (fd4 < 0) ? (fd4 = ip_socket(type, af = AF_INET )) : fd4; + fd = Ustrchr(h->address, ':') != 0 + ? fd6 < 0 ? (fd6 = ip_socket(type, af = AF_INET6)) : fd6 + : fd4 < 0 ? (fd4 = ip_socket(type, af = AF_INET )) : fd4; if (fd < 0) { @@ -337,7 +336,8 @@ for (h = &shost; h != NULL; h = h->next) { if (fd != fd6) close(fd6); if (fd != fd4) close(fd4); - if (connhost) { + if (connhost) + { h->port = port; *connhost = *h; connhost->next = NULL; @@ -354,6 +354,63 @@ bad: } +int +ip_tcpsocket(const uschar * hostport, uschar ** errstr, int tmo) +{ +int scan; +uschar hostname[256]; +unsigned int portlow, porthigh; + +/* extract host and port part */ +scan = sscanf(CS hostport, "%255s %u-%u", hostname, &portlow, &porthigh); +if (scan != 3) + { + if (scan != 2) + { + *errstr = string_sprintf("invalid socket '%s'", hostport); + return -1; + } + porthigh = portlow; + } + +return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh, + tmo, NULL, errstr); +} + +int +ip_unixsocket(const uschar * path, uschar ** errstr) +{ +int sock; +struct sockaddr_un server; + +if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + *errstr = US"can't open UNIX socket."; + return -1; + } + +callout_address = string_copy(path); +server.sun_family = AF_UNIX; +Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1); +server.sun_path[sizeof(server.sun_path)-1] = '\0'; +if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) + { + int err = errno; + (void)close(sock); + *errstr = string_sprintf("unable to connect to UNIX socket (%s): %s", + path, strerror(err)); + return -1; + } +return sock; +} + +int +ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo) +{ +return *spec == '/' + ? ip_unixsocket(spec, errstr) : ip_tcpsocket(spec, errstr, tmo); +} + /************************************************* * Set keepalive on a socket * *************************************************/ @@ -369,7 +426,7 @@ Returns: nothing */ void -ip_keepalive(int sock, uschar *address, BOOL torf) +ip_keepalive(int sock, const uschar *address, BOOL torf) { int fodder = 1; if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, @@ -395,23 +452,22 @@ BOOL fd_ready(int fd, int timeout) { fd_set select_inset; -struct timeval tv; time_t start_recv = time(NULL); +int time_left = timeout; int rc; -if (timeout <= 0) +if (time_left <= 0) { errno = ETIMEDOUT; return FALSE; } /* Wait until the socket is ready */ -for (;;) +do { + struct timeval tv = { time_left, 0 }; FD_ZERO (&select_inset); FD_SET (fd, &select_inset); - tv.tv_sec = timeout; - tv.tv_usec = 0; /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/ rc = select(fd + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv); @@ -423,17 +479,17 @@ for (;;) Aug 2004: Somebody set up a cron job that ran exiwhat every 2 minutes, making the interrupt not at all rare. Since the timeout is typically more than 2 minutes, the effect was to block the timeout completely. To prevent this - happening again, we do an explicit time test. */ + happening again, we do an explicit time test and adjust the timeout + accordingly */ if (rc < 0 && errno == EINTR) { DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n"); - if (time(NULL) - start_recv < timeout) continue; - DEBUG(D_transport) debug_printf("total wait time exceeds timeout\n"); - } - /* Handle a timeout, and treat any other select error as a timeout, including - an EINTR when we have been in this loop for longer than timeout. */ + /* Watch out, 'continue' jumps to the condition, not to the loops top */ + time_left = timeout - (time(NULL) - start_recv); + if (time_left > 0) continue; + } if (rc <= 0) { @@ -441,10 +497,10 @@ for (;;) return FALSE; } - /* If the socket is ready, break out of the loop. */ - - if (FD_ISSET(fd, &select_inset)) break; + /* Checking the FD_ISSET is not enough, if we're interrupted, the + select_inset may still contain the 'input'. */ } +while (rc < 0 || !FD_ISSET(fd, &select_inset)); return TRUE; } @@ -647,13 +703,9 @@ while (last > first) return TRUE; } else if (c > 0) - { first = middle + 1; - } else - { last = middle; - } } return FALSE; } @@ -668,3 +720,5 @@ for (i=0; i < dscp_table_size; ++i) /* End of ip.c */ +/* vi: aw ai sw=2 +*/