X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fip.c;h=b0c98878b6736b9f3365129d50a11ee10a22af4a;hb=817d9f576cd;hp=63645f13942a1e3912cb740adff2412c777bbf60;hpb=f1e894f37fb99398f7447220925a915bd031491a;p=exim.git diff --git a/src/src/ip.c b/src/src/ip.c index 63645f139..b0c98878b 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/ip.c,v 1.3 2005/06/27 14:29:43 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2005 */ +/* 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 @@ -224,17 +224,11 @@ alarm(0); /* There is a testing facility for simulating a connection timeout, as I can't think of any other way of doing this. It converts a connection refused -into a timeout. - -I had to add a second fudge to keep the tests working. Attempts to connect to -10.x.x.x are expected to timeout, but sometimes they now give "No route to -host". */ +into a timeout if the timeout is set to 999999. */ if (running_in_test_harness) { - if ((save_errno == ECONNREFUSED && timeout == 999999) || - (save_errno == EHOSTUNREACH && timeout > 0 && - Ustrncmp(address, "10.", 3) == 0)) + if (save_errno == ECONNREFUSED && timeout == 999999) { rc = -1; save_errno = EINTR; @@ -249,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; } @@ -354,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); @@ -366,4 +361,181 @@ 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; + } +else if (af == AF_INET6) + { + *level = IPPROTO_IPV6; + *optname = IPV6_TCLASS; + } +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 */