/* * network.c -- handles stuff dealing with connecting and name resolving * * Copyright 1995, 2007 Jeremy Nelson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notices, the above paragraph (the one permitting redistribution), * this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The names of the author(s) may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "irc.h" #include "ircaux.h" #include "vars.h" #include "newio.h" #include "output.h" #include /* This will eventually become a configurable */ #ifndef NO_JOB_CONTROL #define ASYNC_DNS #endif static int Connect (int, SSu *); static socklen_t socklen (SSu *); static int Getnameinfo (SSu *, socklen_t, char *, size_t, char *, size_t, int); static int Socket (int, int, int); /* Retval Meaning -------- ------------------------------------------- -1 System call error occured (check errno) -2 The operation isn't supported for the requested family. -3 The hostname has an address, but not in the family you want. -4 The "address presentation" hostname can't be converted. -5 The hostname does not resolve. -6 The requested family does not have a virtual host. -7 The remote sockaddr to connect to was not provided. -8 The local sockaddr and remote sockaddr are different families. -9 The timeout expired before the connection was established. -10 The requested local address is in use or not available. -11 The connect()ion failed (at connect() time) -12 The connection could not be established (after connect() time) -13 The local sockaddr to bind was not provided. -14 The family request does not make sense. */ /*****************************************************************************/ /* * NAME: client_connect * USAGE: Create a new socket and establish both endpoints with the * arguments given. * ARGS: l - A local sockaddr structure representing the local side of * the connection. NULL is permitted. If this value is NULL, * then the local side of the connection will not be bind()ed. * ll - The sizeof(l) -- if 0, then 'l' is treated as a NULL value. * r - A remote sockaddr structure representing the remote side of * the connection. NULL is not permitted. * rl - The sizeof(r) -- if 0, then 'r' is treated as a NULL value. * Therefore, 0 is not permitted. */ int client_connect (SSu *l, socklen_t ll, SSu *r, socklen_t rl) { int fd = -1; int family_ = AF_UNSPEC; if (ll == 0) l = NULL; if (rl == 0) r = NULL; if (!r) { syserr(-1, "client_connect: remote addr missing (connect to who?)"); return -1; } if (l && r && family(l) != family(r)) { syserr(-1, "client_connect: local addr protocol (%d) is different " "from remote addr protocol (%d)", family(l), family(r)); return -1; } if (l) family_ = family(l); if (r) family_ = family(r); if ((fd = Socket(family_, SOCK_STREAM, 0)) < 0) { syserr(-1, "client_connect: socket(%d) failed: %s", family_, strerror(errno)); return -1; } if (l && bind(fd, &l->sa, ll)) { syserr(-1, "client_connect: bind(%d) failed: %s", fd, strerror(errno)); close(fd); return -1; } if (Connect(fd, r)) { syserr(-1, "client_connect: connect(%d) failed: %s", fd, strerror(errno)); close(fd); return -1; } if (x_debug & DEBUG_SERVER_CONNECT) yell("Connected on des [%d]", fd); return fd; } /*****************************************************************************/ /* * NAME: ip_bindery * USAGE: Establish a local passive (listening) socket at the given port. * ARGS: family - AF_INET is the only supported argument. * port - The port to establish the connection upon. May be 0, * which requests any open port. * storage - Pointer to a sockaddr structure big enough for the * specified family. Upon success, it will be filled with * the local sockaddr of the new connection. * NOTES: In most cases, the local address for 'storage' will be INADDR_ANY * which doesn't really give you any useful information. It is not * possible to authoritatively figure out the local IP address * without using ioctl(). However, we guess the best we may. * * NOTES: This function lacks IPv6 support. * This function lacks Unix Domain Socket support. */ int ip_bindery (int family, unsigned short port, SSu *storage) { socklen_t len; int fd; if (inet_vhostsockaddr(family, port, NULL, storage, &len)) { syserr(-1, "ip_bindery: inet_vhostsockaddr(%d,%d) failed.", family, port); return -1; } if (!len) { syserr(-1, "ip_bindery: inet_vhostsockaddr(%d,%d) didn't " "return an address I could bind", family, port); return -1; } if ((fd = client_bind(storage, len)) < 0) { syserr(-1, "ip_bindery: client_bind(%d,%d) failed.", family, port); return -1; } return fd; } /* * NAME: client_bind * USAGE: Create a new socket and establish one endpoint with the arguments * given. * ARGS: local - A local sockaddr structure representing the local side of * the connection. NULL is not permitted. * local_len - The sizeof(l) -- if 0, then 'l' is treated as a NULL * value. Therefore, 0 is not permitted. */ int client_bind (SSu *local, socklen_t local_len) { int fd = -1; int family_ = AF_UNSPEC; if (local_len == 0) local = NULL; if (!local) { syserr(-1, "client_bind: address to bind to not provided"); return -1; } family_ = family(local); if ((fd = Socket(family_, SOCK_STREAM, 0)) < 0) { syserr(-1, "client_bind: socket(%d) failed: %s", family_, strerror(errno)); return -1; } #ifdef IP_PORTRANGE /* * On 4.4BSD systems, this socket option asks the * operating system to select a port from the "high * port range" when we ask for port 0 (any port). * Maybe some day Linux will support this. */ if (family_ == AF_INET && getenv("EPIC_USE_HIGHPORTS")) { int ports = IP_PORTRANGE_HIGH; setsockopt(fd, IPPROTO_IP, IP_PORTRANGE, (char *)&ports, sizeof(ports)); } #endif if (bind(fd, &local->sa, local_len)) { syserr(-1, "client_bind: bind(%d) failed: %s", fd, strerror(errno)); close(fd); return -1; } /* * Get the local sockaddr of the passive socket, * specifically the port number, and stash it in * 'port' which is the return value to the caller. */ if (getsockname(fd, &local->sa, &local_len)) { syserr(-1, "client_bind: getsockname(%d) failed: %s", fd, strerror(errno)); close(fd); return -1; } if (listen(fd, 4) < 0) { syserr(-1, "client_bind: listen(%d,4) failed: %s", fd, strerror(errno)); close(fd); return -1; } return fd; } /*****************************************************************************/ /* * NAME: inet_vhostsockaddr * USAGE: Get the sockaddr of the current virtual host, if one is in use. * ARGS: family - The family whose sockaddr info is to be retrieved * storage - Pointer to a sockaddr structure appropriate for family. * len - This will be set to the size of the sockaddr structure that * was copied, or set to 0 if no virtual host is available in * the given family. * NOTES: If "len" is set to 0, do not attempt to bind() 'storage'! */ int inet_vhostsockaddr (int family, int port, const char *wanthost, SSu *storage, socklen_t *len) { char p_port[12]; char *p = NULL; AI hints, *res; int err; const char *lhn; /* * If port == -1, AND "wanthost" is NULL, then this is a client connection, * so we punt if there is no virtual host name. But if port is NOT zero, * then the caller expects us to return a sockaddr they can bind() to, * so we need to use LocalIPv(4|6)HostName, even if it's NULL. If you * return *len == 0 for port != -1, then /dcc breaks. */ if ((family == AF_UNIX) || (family == AF_INET && port == -1 && empty(wanthost) && LocalIPv4HostName == NULL) || (family == AF_INET6 && port == -1 && empty(wanthost) && LocalIPv6HostName == NULL)) { *len = 0; return 0; /* No vhost needed */ } if (wanthost && *wanthost) lhn = wanthost; else if (family == AF_INET) lhn = LocalIPv4HostName; else if (family == AF_INET6) lhn = LocalIPv6HostName; else lhn = NULL; /* * Can it really be this simple? */ memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; if (port != -1) { hints.ai_flags = AI_PASSIVE; snprintf(p_port, 12, "%d", port); p = p_port; } if ((err = my_getaddrinfo(lhn, p, &hints, &res))) { syserr(-1, "inet_vhostsockaddr: my_getaddrinfo(%s,%s) failed: %s", lhn, p, gai_strerror(err)); return -1; } memcpy(&storage->ss, res->ai_addr, res->ai_addrlen); my_freeaddrinfo(res); *len = socklen(storage); return 0; } /************************************************************************/ /* * NAME: inet_strton * USAGE: Convert "any" kind of address represented by a string into * a socket address suitable for connect()ing or bind()ing with. * ARGS: hostname - The address to convert. It may be any of the following: * IPv4 "Presentation Address" (A.B.C.D) * IPv6 "Presentation Address" (A:B::C:D) * Hostname (foo.bar.com) * 32 bit host order ipv4 address (2134546324) * storage - A pointer to a (union SSU_ (aka, a (struct sockaddr_storage)) with the * "family" argument filled in (AF_INET or AF_INET6). * If "hostname" is a p-addr, then the form of the p-addr * must agree with the family in 'storage'. */ int inet_strton (const char *host, const char *port, SSu *storage_, int flags) { int family_ = family(storage_); /* First check for legacy 32 bit integer DCC addresses */ if ((family_ == AF_INET || family_ == AF_UNSPEC) && host && is_number(host)) { (storage_->si).sin_family = AF_INET; #ifdef HAVE_SA_LEN (storage_->si).sin_len = sizeof(struct sockaddr_in); #endif (storage_->si).sin_addr.s_addr = htonl(strtoul(host, NULL, 10)); if (port) (storage_->si).sin_port = htons((unsigned short)strtoul(port, NULL, 10)); return 0; } else { AI hints; AI *results; int retval; memset(&hints, 0, sizeof(hints)); hints.ai_flags = flags; hints.ai_family = family_; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; if ((retval = my_getaddrinfo(host, port, &hints, &results))) { syserr(-1, "inet_strton: my_getaddrinfo(%s,%s) failed: %s", host, port, gai_strerror(retval)); return -1; } /* memcpy can bite me. */ memcpy(&(storage_->ss), results->ai_addr, results->ai_addrlen); my_freeaddrinfo(results); return 0; } } /* * NAME: inet_ntostr * USAGE: Convert a "sockaddr name" (SA) into a Hostname/p-addr * PLAIN ENGLISH: Convert getpeername() into "foo.bar.com" * ARGS: name - The socket address, possibly returned by getpeername(). * retval - A string to store the hostname/paddr (RETURN VALUE) * size - The length of 'retval' in bytes * RETURN VALUE: "retval" is returned upon success * "empty_string" is returned for any error. * * NOTES: 'flags' should be set to NI_NAMEREQD if you don't want the remote * host's p-addr if it does not have a DNS hostname. */ int inet_ntostr (SSu *name_, char *host, int hsize, char *port, int psize, int flags) { int retval; socklen_t len; len = socklen(name_); if ((retval = Getnameinfo(name_, len, host, hsize, port, psize, flags | NI_NUMERICSERV))) { syserr(-1, "inet_ntostr: Getnameinfo(sockaddr->p_addr) failed: %s", gai_strerror(retval)); return -1; } return 0; } /* * NAME: inet_sa_to_paddr * USAGE: Convert a 'sockaddr name' (SA) into a p-addr * PLAIN ENGLISH: Convert getpeername into "1.2.3.4" * ARGS: name - The socket address, usually returned by getpeername(), * flags - extra flags you want to pass to getnameinfo() * note, NI_NUMERICHOST is always included. */ char * inet_ssu_to_paddr (SSu *name, int flags) { char buffer[8192]; int gni_retval; socklen_t len; len = socklen(name); if ((gni_retval = Getnameinfo(name, len, buffer, sizeof(buffer), NULL, 0, flags | NI_NUMERICHOST))) { syserr(-1, "inet_sa_to_paddr: Getnameinfo(sockaddr->p_addr) failed: %s", gai_strerror(gni_retval)); return malloc_strdup(""); } return malloc_strdup(buffer); } /* * * * * * * * * */ /* Only used by $convert() and $nametoip() */ /* * NAME: inet_hntop * USAGE: Convert a Hostname into a "presentation address" (p-addr) * PLAIN ENGLISH: Convert "A.B.C.D" into "foo.bar.com" * ARGS: family - The family whose presesentation address format to use * host - The hostname to convert * retval - A string to store the p-addr (RETURN VALUE) * size - The length of 'retval' in bytes * RETURN VALUE: "retval" is returned upon success * "empty_string" is returned for any error. */ int inet_hntop (int family, const char *host, char *retval, int size) { SSu buffer; buffer.sa.sa_family = family; if (inet_strton(host, NULL, &buffer, AI_ADDRCONFIG)) { syserr(-1, "inet_hntop: inet_strton(%d,%s) failed", family, host); return -1; } if (inet_ntostr(&buffer, retval, size, NULL, 0, NI_NUMERICHOST)) { syserr(-1, "inet_hntop: inet_ntostr(%d,%s) failed", family, host); return -1; } return 0; } /* Only used by $convert() and $iptoname() */ /* * NAME: inet_ptohn * USAGE: Convert a "presentation address" (p-addr) into a Hostname * PLAIN ENGLISH: Convert "foo.bar.com" into "A.B.C.D" * ARGS: family - The family whose presesentation address format to use * ip - The presentation-address to look up * retval - A string to store the hostname (RETURN VALUE) * size - The length of 'retval' in bytes * RETURN VALUE: "retval" is returned upon success * "empty_string" is returned for any error. */ int inet_ptohn (int family, const char *ip, char *retval, int size) { SSu buffer; memset((char *)&buffer.ss, 0, sizeof(buffer.ss)); buffer.sa.sa_family = family; if (inet_strton(ip, NULL, &buffer, AI_NUMERICHOST)) { syserr(-1, "inet_ptohn: inet_strton(%d,%s) failed", family, ip); return -1; } if (inet_ntostr(&buffer, retval, size, NULL, 0, NI_NAMEREQD)) { syserr(-1, "inet_ptohn: inet_ntostr(%d,%s) failed", family, ip); return -1; } return 0; } /* Only used by $convert() */ /* * NAME: one_to_another * USAGE: Convert a p-addr to a Hostname, or a Hostname to a p-addr. * PLAIN ENGLISH: Convert "A.B.C.D" to "foo.bar.com" or convert "foo.bar.com" * into "A.B.C.D" * ARGS: family - The address family in which to convert (AF_INET/AF_INET6) * what - Either a Hostname or a p-addr. * retval - If "what" is a Hostname, a place to store the p-addr * If "what" is a p-addr, a place to store the Hostname * size - The length of 'retval' in bytes * RETURN VALUE: "retval" is returned upon success * "empty_string" is returned for any error. * * NOTES: If "what" is a p-addr and there is no hostname associated with that * address, that is considered an error and empty_string is returned. */ int one_to_another (int family, const char *what, char *retval, int size) { /* XXX I wish this wasn't necessary */ int old_window_display; old_window_display = swap_window_display(0); if (inet_ptohn(family, what, retval, size)) { if (inet_hntop(family, what, retval, size)) { swap_window_display(old_window_display); syserr(-1, "one_to_another: both inet_ptohn and " "inet_hntop failed (%d,%s)", family, what); return -1; } } swap_window_display(old_window_display); return 0; } /****************************************************************************/ int set_non_blocking (int fd) { int flag, rval; #if defined(O_NONBLOCK) flag = O_NONBLOCK; #elif defined(O_NDELAY) flag = O_NDELAY; #elif defined(FIONBIO) flag = 1; #else yell("Sorry! Can't set nonblocking on this system!"); #endif #if defined(O_NONBLOCK) || defined(O_NDELAY) if ((rval = fcntl(fd, F_GETFL, 0)) == -1) { syserr(-1, "set_non_blocking: fcntl(%d, F_GETFL) failed: %s", fd, strerror(errno)); return -1; } if (fcntl(fd, F_SETFL, rval | flag) == -1) { syserr(-1, "set_non_blocking: fcntl(%d, F_SETFL) failed: %s", fd, strerror(errno)); return -1; } #else if (ioctl(fd, FIONBIO, &flag) < 0) { syserr(-1, "set_non_blocking: ioctl(%d, FIONBIO) failed: %s", fd, strerror(errno)); return -1; } #endif return 0; } int set_blocking (int fd) { int flag, rval; #if defined(O_NONBLOCK) flag = O_NONBLOCK; #elif defined(O_NDELAY) flag = O_NDELAY; #elif defined(FIONBIO) flag = 0; #else yell("Sorry! Can't set nonblocking on this system!"); #endif #if defined(O_NONBLOCK) || defined(O_NDELAY) if ((rval = fcntl(fd, F_GETFL, 0)) == -1) { syserr(-1, "set_blocking: fcntl(%d, F_GETFL) failed: %s", fd, strerror(errno)); return -1; } if (fcntl(fd, F_SETFL, rval & ~flag) == -1) { syserr(-1, "set_blocking: fcntl(%d, F_SETFL) failed: %s", fd, strerror(errno)); return -1; } #else if (ioctl(fd, FIONBIO, &flag) < 0) { syserr(-1, "set_blocking: ioctl(%d, FIONBIO) failed: %s", fd, strerror(errno)); return -1; } #endif return 0; } /****************************************************************************/ /* * It is possible for a race condition to exist; such that select() * indicates that a listen()ing socket is able to recieve a new connection * and that a later accept() call will still block because the connection * has been closed in the interim. This wrapper for accept() attempts to * defeat this by making the accept() call nonblocking. */ int my_accept (int s, SSu *addr, socklen_t *addrlen) { int retval; set_non_blocking(s); if ((retval = accept(s, &addr->sa, addrlen)) < 0) syserr(-1, "my_accept: accept(%d) failed: %s", s, strerror(errno)); set_blocking(s); set_blocking(retval); /* Just in case */ return retval; } static int Connect (int fd, SSu *addr) { int retval; set_non_blocking(fd); if ((retval = connect(fd, &addr->sa, socklen(addr)))) { if (errno != EINPROGRESS) syserr(-1, "Connect: connect(%d) failed: %s", fd, strerror(errno)); else retval = 0; } set_blocking(fd); return retval; } /* * XXX - Ugh! Some getaddrinfo()s take AF_UNIX paths as the 'servname' * instead of as the 'nodename'. How heinous! */ int my_getaddrinfo (const char *nodename, const char *servname, const AI *hints, AI **res) { #ifdef GETADDRINFO_DOES_NOT_DO_AF_UNIX int do_af_unix = 0; SSu storage; AI * results; int len; if (nodename && strchr(nodename, '/')) do_af_unix = 1; if (hints && hints->ai_family == AF_UNIX) do_af_unix = 1; if (do_af_unix) { if (!nodename) return EAI_NONAME; memset(&storage.su, 0, sizeof(storage.su)); storage.su.sun_family = AF_UNIX; strlcpy(storage.su.sun_path, nodename, sizeof(storage.su.sun_path)); #ifdef HAVE_SA_LEN # ifdef SUN_LEN storage.su.sun_len = SUN_LEN(&storage.su); # else storage.su.sun_len = strlen(nodename) + 1; # endif #endif len = strlen(storage.su.sun_path) + 3; (*res) = new_malloc(sizeof(*results)); (*res)->ai_flags = 0; (*res)->ai_family = AF_UNIX; (*res)->ai_socktype = SOCK_STREAM; (*res)->ai_protocol = 0; (*res)->ai_addrlen = len; (*res)->ai_canonname = malloc_strdup(nodename); (*res)->ai_addr = new_malloc(sizeof(storage.su)); memcpy((*res)->ai_addr, &storage.su, sizeof(storage.su)); (*res)->ai_next = 0; return 0; } #endif /* * XXX -- Support getaddrinfo()s that want an AF_UNIX path to * be the second argument and not the first one. Bleh. */ if ((nodename && strchr(nodename, '/')) || (hints && hints->ai_family == AF_UNIX)) return getaddrinfo(NULL, nodename, hints, res); else return getaddrinfo(nodename, servname, hints, res); } void my_freeaddrinfo (AI *ai) { #ifdef GETADDRINFO_DOES_NOT_DO_AF_UNIX if (ai->ai_family == AF_UNIX) { new_free(&ai->ai_canonname); new_free(&ai->ai_addr); new_free(&ai); return; } #endif freeaddrinfo(ai); } static int Getnameinfo(SSu *ssu, socklen_t ssulen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) { #ifdef GETADDRINFO_DOES_NOT_DO_AF_UNIX if (family(ssu) == AF_UNIX) { socklen_t len; #ifdef HAVE_SA_LEN len = ssu->su.sun_len; #else # ifdef SUN_LEN len = SUN_LEN(&ssu->su); # else len = strlen(ssu->su.sun_path); # endif #endif if (host && hostlen) { if (hostlen <= len) { memcpy(host, ssu->su.sun_path, hostlen); host[hostlen - 1] = 0; } else { memcpy(host, &ssu->su.sun_path, len); host[len] = 0; } } if (serv && servlen) *serv = 0; return 0; } #endif if ((flags & GNI_INTEGER) && family(ssu) == AF_INET) { snprintf(host, hostlen, "%lu", (unsigned long)ntohl(ssu->si.sin_addr.s_addr)); host = NULL; hostlen = 0; } flags = flags & ~(GNI_INTEGER); return getnameinfo(&ssu->sa, ssulen, host, hostlen, serv, servlen, flags); } static socklen_t socklen (SSu *sockaddr) { if (sockaddr->sa.sa_family == AF_INET) return sizeof(sockaddr->si); else if (sockaddr->sa.sa_family == AF_INET6) return sizeof(sockaddr->si6); else if (sockaddr->sa.sa_family == AF_UNIX) return strlen(sockaddr->su.sun_path) + 2; else return 0; } int family (SSu *sockaddr_) { return (sockaddr_->sa).sa_family; } /* set's socket options */ int Socket (int domain, int type, int protocol) { int opt = 1; int optlen = sizeof(opt); int s; #ifndef NO_STRUCT_LINGER struct linger lin; #endif if ((s = socket(domain, type, protocol)) < 0) { syserr(-1, "Socket: socket(%d,%d,%d) failed: %s", domain, type, protocol, strerror(errno)); return -1; } #ifndef NO_STRUCT_LINGER lin.l_onoff = lin.l_linger = 0; setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&lin, optlen); #endif setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, optlen); opt = 1; setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, optlen); return s; } pid_t async_getaddrinfo (const char *nodename, const char *servname, const AI *hints, int fd) { AI *results = NULL; ssize_t err; #ifdef ASYNC_DNS { /* XXX Letting /exec clean up after us is a hack. */ pid_t helper; if ((helper = fork())) return helper; } #endif if ((err = my_getaddrinfo(nodename, servname, hints, &results))) { err = -labs(err); /* Always a negative number */ if (!write(fd, &err, sizeof(err))) (void) 0; close(fd); #ifdef ASYNC_DNS exit(0); #else return 0; #endif } if (!results) { err = 0; if (!write(fd, &err, sizeof(err))) (void) 0; close(fd); #ifdef ASYNC_DNS exit(0); #else return 0; #endif } marshall_getaddrinfo(fd, results); my_freeaddrinfo(results); close(fd); #ifdef ASYNC_DNS exit(0); #endif return 0; /* XXX This function should be void */ } void marshall_getaddrinfo (int fd, AI *results) { ssize_t len, gah; ssize_t alignment = sizeof(void *); AI *result, *copy; char *retval, *ptr; for (len = 0, result = results; result; result = result->ai_next) { len += sizeof(AI); len += result->ai_addrlen; if (result->ai_canonname) len += strlen(result->ai_canonname) + 1; while (len % alignment != 0) len++; } /* Why do I know I'm gonna regret this? */ ptr = retval = new_malloc(len + 1); memset(retval, 0, len + 1); for (result = results; result; result = result->ai_next) { copy = (AI *)ptr; /* Copy over the AI */ memcpy(copy, result, sizeof(AI)); /* Copy over the ai_addr */ ptr += sizeof(AI); memcpy(ptr, result->ai_addr, result->ai_addrlen); /* Point the AI at the ai_addr */ copy->ai_addr = (struct sockaddr *)(ptr); /* Copy over the canonname */ /* Point the AI at the canonname */ ptr += result->ai_addrlen; if (result->ai_canonname) { gah = strlen(result->ai_canonname) + 1; memcpy(ptr, result->ai_canonname, gah); copy->ai_canonname = ptr; ptr += gah; } else copy->ai_canonname = NULL; /* Calculate the start of the next entry */ while ((intptr_t)ptr % alignment != 0) ptr++; /* Point the AI at the next entry */ if (result->ai_next) copy->ai_next = (AI *)ptr; else copy->ai_next = NULL; } if (!write(fd, (void *)&len, sizeof(len))) (void) 0; if (!write(fd, (void *)retval, len)) (void) 0; new_free(&retval); } void unmarshall_getaddrinfo (AI *results) { ssize_t len; AI *result; char *ptr; ssize_t alignment = sizeof(void *); /* Why do I know I'm gonna regret this? */ for (result = results; result; result = result->ai_next) { ptr = (char *)result; /* Copy over the ai_addr */ ptr += sizeof(AI); result->ai_addr = (struct sockaddr *)(ptr); ptr += result->ai_addrlen; if (result->ai_canonname) { result->ai_canonname = ptr; len = strlen(result->ai_canonname) + 1; ptr += len; } /* Calculate the start of the next entry */ while ((intptr_t)ptr % alignment != 0) ptr++; /* Point the AI at the next entry */ if (result->ai_next) result->ai_next = (AI *)ptr; } }