X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/75e0e026a196fa852a855d5148f29be29ac2d92f..ff2c417d0b970db22a382cb692d066d8fe3c32ae:/src/src/ip.c diff --git a/src/src/ip.c b/src/src/ip.c index 0e4eab650..98eed1b93 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/ip.c,v 1.5 2006/02/16 10:05:33 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2006 */ +/* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for doing things with sockets. With the advent of IPv6 this has @@ -165,7 +163,9 @@ return bind(sock, (struct sockaddr *)&sin, s_len); *************************************************/ /* This function connects a socket to a remote address and port. The socket may -or may not have previously been bound to a local interface. +or may not have previously been bound to a local interface. The socket is not +closed, even in cases of error. It is expected that the calling function, which +created the socket, will be the one that closes it. Arguments: sock the socket @@ -243,7 +243,6 @@ 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. */ -(void)close(sock); errno = (save_errno == EINTR && sigalrm_seen)? ETIMEDOUT : save_errno; return -1; } @@ -348,8 +347,10 @@ for (;;) 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); @@ -360,4 +361,183 @@ return -1; } + + +/************************************************* +* 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 * +*************************************************/ + +struct dscp_name_tableentry { + const uschar *name; + int value; +}; +/* Keep both of these tables sorted! */ +static struct dscp_name_tableentry dscp_table[] = { +#ifdef IPTOS_DSCP_AF11 + { CUS"af11", IPTOS_DSCP_AF11 }, + { CUS"af12", IPTOS_DSCP_AF12 }, + { CUS"af13", IPTOS_DSCP_AF13 }, + { CUS"af21", IPTOS_DSCP_AF21 }, + { CUS"af22", IPTOS_DSCP_AF22 }, + { CUS"af23", IPTOS_DSCP_AF23 }, + { CUS"af31", IPTOS_DSCP_AF31 }, + { CUS"af32", IPTOS_DSCP_AF32 }, + { CUS"af33", IPTOS_DSCP_AF33 }, + { CUS"af41", IPTOS_DSCP_AF41 }, + { CUS"af42", IPTOS_DSCP_AF42 }, + { CUS"af43", IPTOS_DSCP_AF43 }, + { CUS"ef", IPTOS_DSCP_EF }, +#endif +#ifdef IPTOS_LOWCOST + { CUS"lowcost", IPTOS_LOWCOST }, +#endif + { CUS"lowdelay", IPTOS_LOWDELAY }, +#ifdef IPTOS_MINCOST + { CUS"mincost", IPTOS_MINCOST }, +#endif + { CUS"reliability", IPTOS_RELIABILITY }, + { CUS"throughput", IPTOS_THROUGHPUT } +}; +static int dscp_table_size = + 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. 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 + af address_family in use + level setsockopt level to use + optname setsockopt name to use + 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, *p; +int first, last; +long rawlong; + +if (af == AF_INET) + { + *level = IPPROTO_IP; + *optname = IP_TOS; + } +#if HAVE_IPV6 +else if (af == AF_INET6) + { + *level = IPPROTO_IPV6; + *optname = IPV6_TCLASS; + } +#endif +else + { + DEBUG(D_transport) + debug_printf("Unhandled address family %d in dscp_lookup()\n", af); + return FALSE; + } +if (!dscp_name) + { + DEBUG(D_transport) + debug_printf("[empty DSCP]\n"); + return FALSE; + } +dscp_lookup = expand_string(US dscp_name); +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) + { + int middle = (first + last)/2; + int c = Ustrcmp(dscp_lookup, dscp_table[middle].name); + if (c == 0) + { + *dscp_value = dscp_table[middle].value; + return TRUE; + } + else if (c > 0) + { + first = middle + 1; + } + else + { + last = middle; + } + } +return FALSE; +} + +void +dscp_list_to_stream(FILE *stream) +{ +int i; +for (i=0; i < dscp_table_size; ++i) + fprintf(stream, "%s\n", dscp_table[i].name); +} + + /* End of ip.c */