* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2014 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for doing things with sockets. With the advent of IPv6 this has
af AF_INET6 or AF_INET for the socket type
address the remote address, in text form
port the remote port
- timeout a timeout
+ timeout a timeout (zero for indefinite timeout)
Returns: 0 on success; -1 on failure, with errno set
*/
+/*************************************************
+* Create connected socket to remote host *
+*************************************************/
+
+/* Create a socket and connect to host (name or number, ipv6 ok)
+ at one of port-range.
+
+Arguments:
+ type SOCK_DGRAM or SOCK_STREAM
+ af AF_INET6 or AF_INET for the socket type
+ address the remote address, in text form
+ portlo,porthi the remote port range
+ timeout a timeout
+ connhost if not NULL, host_item filled in with connection details
+ errstr pointer for allocated string on error
+
+Return:
+ socket fd, or -1 on failure (having allocated an error string)
+*/
+int
+ip_connectedsocket(int type, const uschar * hostname, int portlo, int porthi,
+ int timeout, host_item * connhost, uschar ** errstr)
+{
+int namelen, port;
+host_item shost;
+host_item *h;
+int af = 0, fd, fd4 = -1, fd6 = -1;
+
+shost.next = NULL;
+shost.address = NULL;
+shost.port = portlo;
+shost.mx = -1;
+
+namelen = Ustrlen(hostname);
+
+/* Anything enclosed in [] must be an IP address. */
+
+if (hostname[0] == '[' &&
+ hostname[namelen - 1] == ']')
+ {
+ uschar * host = string_copy(hostname);
+ host[namelen - 1] = 0;
+ host++;
+ if (string_is_ip_address(host, NULL) == 0)
+ {
+ *errstr = string_sprintf("malformed IP address \"%s\"", hostname);
+ return -1;
+ }
+ shost.name = shost.address = host;
+ }
+
+/* Otherwise check for an unadorned IP address */
+
+else if (string_is_ip_address(hostname, NULL) != 0)
+ shost.name = shost.address = string_copy(hostname);
+
+/* 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)
+ {
+ *errstr = string_sprintf("no IP address found for host %s", shost.name);
+ return -1;
+ }
+ }
+
+/* Try to connect to the server - test each IP till one works */
+
+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;
+
+ if (fd < 0)
+ {
+ *errstr = string_sprintf("failed to create socket: %s", strerror(errno));
+ goto bad;
+ }
+
+ for(port = portlo; port <= porthi; port++)
+ if (ip_connect(fd, af, h->address, port, timeout) == 0)
+ {
+ if (fd != fd6) close(fd6);
+ if (fd != fd4) close(fd4);
+ if (connhost) {
+ h->port = port;
+ *connhost = *h;
+ connhost->next = NULL;
+ }
+ return fd;
+ }
+ }
+
+*errstr = string_sprintf("failed to connect to %s: "
+ "couldn't connect to any host: %s", hostname, strerror(errno));
+
+bad:
+ close(fd4); close(fd6); return -1;
+}
+
+
/*************************************************
* Set keepalive on a socket *
*************************************************/
{
fd_set select_inset;
struct timeval tv;
-int start_recv = time(NULL);
+time_t start_recv = time(NULL);
int rc;
/* Wait until the socket is ready */
close down of the connection), set errno to zero; otherwise leave it alone. */
#ifdef SUPPORT_TLS
-if (tls_active == sock)
- rc = tls_read(buffer, buffsize);
+if (tls_out.active == sock)
+ rc = tls_read(FALSE, buffer, buffsize);
+else if (tls_in.active == sock)
+ rc = tls_read(TRUE, buffer, buffsize);
else
#endif
rc = recv(sock, buffer, buffsize, 0);
+/*************************************************
+* Lookup address family of potential socket *
+*************************************************/
+
+/* Given a file-descriptor, check to see if it's a socket and, if so,
+return the address family; detects IPv4 vs IPv6. If not a socket then
+return -1.
+
+The value 0 is typically AF_UNSPEC, which should not be seen on a connected
+fd. If the return is -1, the errno will be from getsockname(); probably
+ENOTSOCK or ECONNRESET.
+
+Arguments: socket-or-not fd
+Returns: address family or -1
+*/
+
+int
+ip_get_address_family(int fd)
+{
+struct sockaddr_storage ss;
+socklen_t sslen = sizeof(ss);
+
+if (getsockname(fd, (struct sockaddr *) &ss, &sslen) < 0)
+ return -1;
+
+return (int) ss.ss_family;
+}
+
+
+
+
/*************************************************
* Lookup DSCP settings for a socket *
*************************************************/
sizeof(dscp_table) / sizeof(struct dscp_name_tableentry);
/* DSCP values change by protocol family, and so do the options used for
-setsockopt(); this utility does all the lookups.
+setsockopt(); this utility does all the lookups. It takes an unexpanded
+option string, expands it, strips off affix whitespace, then checks if it's
+a number. If all of what's left is a number, then that's how the option will
+be parsed and success/failure is a range check. If it's not all a number,
+then it must be a supported keyword.
Arguments:
dscp_name a string, so far unvalidated
dscp_value value for dscp_name
Returns: TRUE if okay to setsockopt(), else FALSE
+
+*level and *optname may be set even if FALSE is returned
*/
BOOL
dscp_lookup(const uschar *dscp_name, int af,
int *level, int *optname, int *dscp_value)
{
-uschar *dscp_lookup;
+uschar *dscp_lookup, *p;
int first, last;
+long rawlong;
if (af == AF_INET)
{
*level = IPPROTO_IP;
*optname = IP_TOS;
}
+#if HAVE_IPV6 && defined(IPV6_TCLASS)
else if (af == AF_INET6)
{
*level = IPPROTO_IPV6;
*optname = IPV6_TCLASS;
}
+#endif
else
{
DEBUG(D_transport)
if (dscp_lookup == NULL || *dscp_lookup == '\0')
return FALSE;
+p = dscp_lookup + Ustrlen(dscp_lookup) - 1;
+while (isspace(*p)) *p-- = '\0';
+while (isspace(*dscp_lookup) && dscp_lookup < p) dscp_lookup++;
+if (*dscp_lookup == '\0')
+ return FALSE;
+
+rawlong = Ustrtol(dscp_lookup, &p, 0);
+if (p != dscp_lookup && *p == '\0')
+ {
+ /* We have six bits available, which will end up shifted to fit in 0xFC mask.
+ RFC 2597 defines the values unshifted. */
+ if (rawlong < 0 || rawlong > 0x3F)
+ {
+ DEBUG(D_transport)
+ debug_printf("DSCP value %ld out of range, ignored.\n", rawlong);
+ return FALSE;
+ }
+ *dscp_value = rawlong << 2;
+ return TRUE;
+ }
+
first = 0;
last = dscp_table_size;
while (last > first)