diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | ce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch) | |
tree | 5ac38a06f3dde268dc7927dc155896926aaf7012 /kdecore/netsupp.cpp | |
download | tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kdecore/netsupp.cpp')
-rw-r--r-- | kdecore/netsupp.cpp | 1237 |
1 files changed, 1237 insertions, 0 deletions
diff --git a/kdecore/netsupp.cpp b/kdecore/netsupp.cpp new file mode 100644 index 000000000..ee8ddad23 --- /dev/null +++ b/kdecore/netsupp.cpp @@ -0,0 +1,1237 @@ +/* + * This file is part of the KDE libraries + * Copyright (C) 2000,2001 Thiago Macieira <thiago.macieira@kdemail.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + **/ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <arpa/inet.h> + +#include <qglobal.h> + +// This is so that, if addrinfo is defined, it doesn't clobber our definition +// It might be defined in the few cases in which we are replacing the system's +// broken getaddrinfo +#include <netdb.h> + +#include "config.h" +#include "kdebug.h" +#include "klocale.h" + +#ifndef IN6_IS_ADDR_V4MAPPED +#define NEED_IN6_TESTS +#endif +#undef CLOBBER_IN6 +#include "netsupp.h" + +#if defined(__hpux) || defined(_HPUX_SOURCE) +extern int h_errno; +#endif + +#include <kdemacros.h> + +#if !defined(kde_sockaddr_in6) +/* + * kde_sockaddr_in6 might have got defined even though we #undef'ed + * CLOBBER_IN6. This happens when we are compiling under --enable-final. + * However, in that case, if it was defined, that's because ksockaddr.cpp + * had it defined because sockaddr_in6 didn't exist, and so sockaddr_in6 + * exists and is our kde_sockaddr_in6 + */ +# define sockaddr_in6 kde_sockaddr_in6 +# define in6_addr kde_in6_addr +#endif + +#ifdef offsetof +#undef offsetof +#endif +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +/* + * These constants tell the flags in KDE::resolverFlags + * The user could (but shouldn't) test the variable to know what kind of + * resolution is supported + */ +#define KRF_KNOWS_AF_INET6 0x01 /* if present, the code knows about AF_INET6 */ +#define KRF_USING_OWN_GETADDRINFO 0x02 /* if present, we are using our own getaddrinfo */ +#define KRF_USING_OWN_INET_NTOP 0x04 /* if present, we are using our own inet_ntop */ +#define KRF_USING_OWN_INET_PTON 0x08 /* if present, we are using our own inet_pton */ +#define KRF_CAN_RESOLVE_UNIX 0x100 /* if present, the resolver can resolve Unix sockets */ +#define KRF_CAN_RESOLVE_IPV4 0x200 /* if present, the resolver can resolve to IPv4 */ +#define KRF_CAN_RESOLVE_IPV6 0x400 /* if present, the resolver can resolve to IPv6 */ + + +static void dofreeaddrinfo(struct addrinfo *ai) +{ + while (ai) + { + struct addrinfo *ai2 = ai; + if (ai->ai_canonname != NULL) + free(ai->ai_canonname); + + if (ai->ai_addr != NULL) + free(ai->ai_addr); + + ai = ai->ai_next; + free(ai2); + } +} + +void kde_freeaddrinfo(struct kde_addrinfo *ai) +{ + if (ai->origin == KAI_LOCALUNIX) + { + struct addrinfo *p, *last = NULL; + /* We've added one AF_UNIX socket in here, to the + * tail of the linked list. We have to find it */ + for (p = ai->data; p; p = p->ai_next) + { + if (p->ai_family == AF_UNIX) + { + if (last) + { + last->ai_next = NULL; + freeaddrinfo(ai->data); + } + dofreeaddrinfo(p); + break; + } + last = p; + } + } + else + freeaddrinfo(ai->data); + + free(ai); +} + +static struct addrinfo* +make_unix(const char *name, const char *serv) +{ + const char *buf; + struct addrinfo *p; + struct sockaddr_un *_sun; + int len; + + p = (addrinfo*)malloc(sizeof(*p)); + if (p == NULL) + return NULL; + memset(p, 0, sizeof(*p)); + + if (name != NULL) + buf = name; + else + buf = serv; + + // Calculate length of the binary representation + len = strlen(buf) + offsetof(struct sockaddr_un, sun_path) + 1; + if (*buf != '/') + len += 5; // strlen("/tmp/"); + + _sun = (sockaddr_un*)malloc(len); + if (_sun == NULL) + { + // Oops + free(p); + return NULL; + } + + _sun->sun_family = AF_UNIX; +# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + _sun->sun_len = len; +# endif + if (*buf == '/') + *_sun->sun_path = '\0'; // empty it + else + strcpy(_sun->sun_path, "/tmp/"); + strcat(_sun->sun_path, buf); + + // Set the addrinfo + p->ai_family = AF_UNIX; + p->ai_addrlen = len; + p->ai_addr = (sockaddr*)_sun; + p->ai_canonname = strdup(buf); + + return p; +} + +// Ugh. I hate #ifdefs +// Anyways, here's what this does: +// KDE_IPV6_LOOKUP_MODE != 1, this function doesn't exist +// AF_INET6 not defined, we say there is no IPv6 stack +// otherwise, we try to create a socket. +// returns: 1 for IPv6 stack available, 2 for not available +#if defined(KDE_IPV6_LOOKUP_MODE) && KDE_IPV6_LOOKUP_MODE == 1 +static int check_ipv6_stack() +{ +# ifndef AF_INET6 + return 2; // how can we check? +# else + if (getenv("KDE_NO_IPV6")) + return 2; + int fd = ::socket(AF_INET6, SOCK_STREAM, 0); + if (fd == -1) + return 2; + + ::close(fd); + return 1; +# endif +} +#endif + + +/* + * Reason for using this function: kde_getaddrinfo + * + * I decided to add this wrapper function for getaddrinfo + * and have this be called by KExtendedSocket instead of + * the real getaddrinfo so that we can make sure that the + * behavior is the desired one. + * + * Currently, the only "undesired" behavior is getaddrinfo + * not returning PF_UNIX sockets in some implementations. + * + * getaddrinfo and family are defined in POSIX 1003.1g + * (Protocol Independent Interfaces) and in RFC 2553 + * (Basic Socket Interface for IPv6). Whereas the RFC is ambiguosly + * vague whether this family of functions should return Internet + * sockets only or not, the name of the POSIX draft says + * otherwise: it should be independent of protocol. + * + * So, my interpretation is that they should return every + * kind of socket available and known and that's how I + * designed KExtendedSocket on top of it. + * + * That's why there's this wrapper, to make sure PF_UNIX + * sockets are returned when expected. + */ + +int kde_getaddrinfo(const char *name, const char *service, + const struct addrinfo* hint, + struct kde_addrinfo** result) +{ + struct kde_addrinfo* res; + struct addrinfo* p; + int err = EAI_SERVICE; +#if defined(KDE_IPV6_LOOKUP_MODE) && KDE_IPV6_LOOKUP_MODE == 1 + // mode 1: do a check on whether we have an IPv6 stack + static int ipv6_stack = 0; // 0: unknown, 1: yes, 2: no +#endif + + // allocate memory for results + res = (kde_addrinfo*)malloc(sizeof(*res)); + if (res == NULL) + return EAI_MEMORY; + res->data = NULL; + res->origin = KAI_SYSTEM; // at first, it'll be only system data + + struct addrinfo* last = NULL; + + // Skip the getaddrinfo call and the ipv6 check for a UNIX socket. + if (hint && (hint->ai_family == PF_UNIX)) + { + if (service == NULL || *service == '\0') + goto out; // can't be Unix if no service was requested + + // Unix sockets must be localhost + // That is, either name is NULL or, if it's not, it must be empty, + // "*" or "localhost" + if (name != NULL && !(name[0] == '\0' || (name[0] == '*' && name[1] == '\0') || + strcmp("localhost", name) == 0)) + goto out; // isn't localhost + + goto do_unix; + } + +#if defined(KDE_IPV6_LOOKUP_MODE) && KDE_IPV6_LOOKUP_MODE != 0 +# if KDE_IPV6_LOOKUP_MODE == 1 + // mode 1: do a check on whether we have an IPv6 stack + if (ipv6_stack == 0) + ipv6_stack = check_ipv6_stack(); + + if (ipv6_stack == 2) + { +# endif + // here we have modes 1 and 2 (no lookups) + // this is shared code + struct addrinfo our_hint; + if (hint != NULL) + { + memcpy(&our_hint, hint, sizeof(our_hint)); + if (our_hint.ai_family == AF_UNSPEC) + our_hint.ai_family = AF_INET; + } + else + { + memset(&our_hint, 0, sizeof(our_hint)); + our_hint.ai_family = AF_INET; + } + + // do the actual resolution + err = getaddrinfo(name, service, &our_hint, &res->data); +# if KDE_IPV6_LOOKUP_MODE == 1 + } + else +# endif +#endif +#if defined(KDE_IPV6_LOOKUP_MODE) && KDE_IPV6_LOOKUP_MODE != 2 + // do the IPV6 resolution + err = getaddrinfo(name, service, hint, &res->data); +#endif + + // Now we have to check whether the user could want a Unix socket + + if (service == NULL || *service == '\0') + goto out; // can't be Unix if no service was requested + + // Unix sockets must be localhost + // That is, either name is NULL or, if it's not, it must be empty, + // "*" or "localhost" + if (name != NULL && !(name[0] == '\0' || (name[0] == '*' && name[1] == '\0') || + strcmp("localhost", name) == 0)) + goto out; // isn't localhost + + // Unix sockets can only be returned if the user asked for a PF_UNSPEC + // or PF_UNIX socket type or gave us a NULL hint + if (hint != NULL && (hint->ai_family != PF_UNSPEC && hint->ai_family != PF_UNIX)) + goto out; // user doesn't want Unix + + // If we got here, then it means that the user might be expecting Unix + // sockets. The user wants a local socket, with a non-null service and + // has told us that they accept PF_UNIX sockets + // Check whether the system implementation returned Unix + if (err == 0) + for (p = res->data; p; p = p->ai_next) + { + last = p; // we have to find out which one is last anyways + if (p->ai_family == AF_UNIX) + // there is an Unix node + goto out; + } + + do_unix: + // So, give the user a PF_UNIX socket + p = make_unix(NULL, service); + if (p == NULL) + { + err = EAI_MEMORY; + goto out; + } + if (hint != NULL) + p->ai_socktype = hint->ai_socktype; + if (p->ai_socktype == 0) + p->ai_socktype = SOCK_STREAM; // default + + if (last) + last->ai_next = p; + else + res->data = p; + res->origin = KAI_LOCALUNIX; + *result = res; + return 0; + + out: + if (res->data != NULL) + freeaddrinfo(res->data); + free(res); + return err; +} + +#if defined(HAVE_GETADDRINFO) && !defined(HAVE_BROKEN_GETADDRINFO) + +#define KRF_getaddrinfo 0 +#define KRF_resolver 0 + +#else // !defined(HAVE_GETADDRINFO) || defined(HAVE_BROKEN_GETADDRINFO) + +#define KRF_getaddrinfo KRF_USING_OWN_GETADDRINFO +#define KRF_resolver KRF_CAN_RESOLVE_UNIX | KRF_CAN_RESOLVE_IPV4 + +/* + * No getaddrinfo() in this system. + * We shall provide our own + */ + +/** TODO + * Try and use gethostbyname2_r before gethostbyname2 and gethostbyname + */ +static int inet_lookup(const char *name, int portnum, int protonum, + struct addrinfo *p, const struct addrinfo *hint, + struct addrinfo** result) +{ + struct addrinfo *q; + struct hostent *h; + struct sockaddr **psa = NULL; + int len; + + // TODO + // Currently, this never resolves IPv6 (need gethostbyname2, etc.) +# ifdef AF_INET6 + if (hint->ai_family == AF_INET6) + { + if (p != NULL) + { + *result = p; + return 0; + } + return EAI_FAIL; + } +# endif + + q = (addrinfo*)malloc(sizeof(*q)); + if (q == NULL) + { + freeaddrinfo(p); + return EAI_MEMORY; + } + + h = gethostbyname(name); + if (h == NULL) + { + if (p != NULL) + { + // There already is a suitable result + *result = p; + return 0; + } + + switch (h_errno) + { + case HOST_NOT_FOUND: + return EAI_NONAME; + case TRY_AGAIN: + return EAI_AGAIN; + case NO_RECOVERY: + return EAI_FAIL; + case NO_ADDRESS: + return EAI_NODATA; + default: + // EH!? + return EAI_FAIL; + } + } + + // convert the hostent to addrinfo + if (h->h_addrtype == AF_INET && (hint->ai_family == AF_INET || hint->ai_family == AF_UNSPEC)) + len = sizeof(struct sockaddr_in); +# ifdef AF_INET6 + else if (h->h_addrtype == AF_INET6 && (hint->ai_family == AF_INET6 || + hint->ai_family == AF_UNSPEC)) + len = sizeof(struct sockaddr_in6); +# endif + else + { + // We don't know what to do with these addresses + // Or gethostbyname returned information we don't want + if (p != NULL) + { + *result = p; + return 0; + } + return EAI_NODATA; + } + + q->ai_flags = 0; + q->ai_family = h->h_addrtype; + q->ai_socktype = hint->ai_socktype; + q->ai_protocol = protonum; + q->ai_addrlen = len; + + q->ai_addr = (sockaddr*)malloc(len); + if (q->ai_addr == NULL) + { + free(q); + freeaddrinfo(p); + return EAI_MEMORY; + } + if (h->h_addrtype == AF_INET) + { + struct sockaddr_in *sin = (sockaddr_in*)q->ai_addr; + sin->sin_family = AF_INET; +# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + sin->sin_len = sizeof(*sin); +# endif + sin->sin_port = portnum; + memcpy(&sin->sin_addr, h->h_addr, h->h_length); + } +# ifdef AF_INET6 + else if (h->h_addrtype == AF_INET6) + { + struct sockaddr_in6 *sin6 = (sockaddr_in6*)q->ai_addr; + sin6->sin6_family = AF_INET6; +# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + sin6->sin6_len = sizeof(*sin6); +# endif + sin6->sin6_port = portnum; + sin6->sin6_flowinfo = 0; + memcpy(&sin6->sin6_addr, h->h_addr, h->h_length); + sin6->sin6_scope_id = 0; + } +# endif + + if (hint->ai_flags & AI_CANONNAME) + q->ai_canonname = strdup(h->h_name); + else + q->ai_canonname = NULL; + + q->ai_next = p; + p = q; + + // cycle through the rest of the hosts; + for (psa = (sockaddr**)h->h_addr_list + 1; *psa; psa++) + { + q = (addrinfo*)malloc(sizeof(*q)); + if (q == NULL) + { + freeaddrinfo(p); + return EAI_MEMORY; + } + memcpy(q, p, sizeof(*q)); + + q->ai_addr = (sockaddr*)malloc(h->h_length); + if (q->ai_addr == NULL) + { + freeaddrinfo(p); + free(q); + return EAI_MEMORY; + } + if (h->h_addrtype == AF_INET) + { + struct sockaddr_in *sin = (sockaddr_in*)q->ai_addr; + sin->sin_family = AF_INET; +# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + sin->sin_len = sizeof(*sin); +# endif + sin->sin_port = portnum; + memcpy(&sin->sin_addr, *psa, h->h_length); + } +# ifdef AF_INET6 + else if (h->h_addrtype == AF_INET6) + { + struct sockaddr_in6 *sin6 = (sockaddr_in6*)q->ai_addr; + sin6->sin6_family = AF_INET6; +# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + sin6->sin6_len = sizeof(*sin6); +# endif + sin6->sin6_port = portnum; + sin6->sin6_flowinfo = 0; + memcpy(&sin6->sin6_addr, *psa, h->h_length); + sin6->sin6_scope_id = 0; + } +# endif + + if (q->ai_canonname != NULL) + q->ai_canonname = strdup(q->ai_canonname); + + q->ai_next = p; + p = q; + } + + *result = p; + return 0; // Whew! Success! +} + +static int make_inet(const char *name, int portnum, int protonum, struct addrinfo *p, + const struct addrinfo *hint, struct addrinfo** result) +{ + struct addrinfo *q; + + do + { + // This 'do' is here just so that we can 'break' out of it + + if (name != NULL) + { + // first, try to use inet_pton before resolving + // it will catch IP addresses given without having to go to lookup + struct sockaddr_in *sin; + struct in_addr in; +# ifdef AF_INET6 + struct sockaddr_in6 *sin6; + struct in6_addr in6; + + if (hint->ai_family == AF_INET6 || (hint->ai_family == AF_UNSPEC && + strchr(name, ':') != NULL)) + { + // yes, this is IPv6 + if (inet_pton(AF_INET6, name, &in6) != 1) + { + if (hint->ai_flags & AI_NUMERICHOST) + { + freeaddrinfo(p); + return EAI_FAIL; + } + break; // not a numeric host + } + + sin6 = (sockaddr_in6*)malloc(sizeof(*sin6)); + if (sin6 == NULL) + { + freeaddrinfo(p); + return EAI_MEMORY; + } + memcpy(&sin6->sin6_addr, &in6, sizeof(in6)); + + if (strchr(name, '%') != NULL) + { + errno = 0; + sin6->sin6_scope_id = strtoul(strchr(name, '%') + 1, NULL, 10); + if (errno != 0) + sin6->sin6_scope_id = 0; // no interface + } + + q = (addrinfo*)malloc(sizeof(*q)); + if (q == NULL) + { + freeaddrinfo(p); + free(sin6); + return EAI_MEMORY; + } + + sin6->sin6_family = AF_INET6; +# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + sin6->sin6_len = sizeof(*sin6); +# endif + sin6->sin6_port = portnum; + sin6->sin6_flowinfo = 0; + + q->ai_flags = 0; + q->ai_family = AF_INET6; + q->ai_socktype = hint->ai_socktype; + q->ai_protocol = protonum; + q->ai_addrlen = sizeof(*sin6); + q->ai_canonname = NULL; + q->ai_addr = (sockaddr*)sin6; + q->ai_next = p; + + *result = q; + return 0; // success! + } +# endif // AF_INET6 + + if (hint->ai_family == AF_INET || hint->ai_family == AF_UNSPEC) + { + // This has to be IPv4 + if (inet_pton(AF_INET, name, &in) != 1) + { + if (hint->ai_flags & AI_NUMERICHOST) + { + freeaddrinfo(p); + return EAI_FAIL; // invalid, I guess + } + break; // not a numeric host, do lookup + } + + sin = (sockaddr_in*)malloc(sizeof(*sin)); + if (sin == NULL) + { + freeaddrinfo(p); + return EAI_MEMORY; + } + + q = (addrinfo*)malloc(sizeof(*q)); + if (q == NULL) + { + freeaddrinfo(p); + free(sin); + return EAI_MEMORY; + } + + sin->sin_family = AF_INET; +# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + sin->sin_len = sizeof(*sin); +# endif + sin->sin_port = portnum; + sin->sin_addr = in; + + q->ai_flags = 0; + q->ai_family = AF_INET; + q->ai_socktype = hint->ai_socktype; + q->ai_protocol = protonum; + q->ai_addrlen = sizeof(*sin); + q->ai_canonname = NULL; + q->ai_addr = (sockaddr*)sin; + q->ai_next = p; + *result = q; + return 0; + } + + // Eh, what!? + // One of the two above has to have matched + kdError() << "I wasn't supposed to get here!"; + } + } while (false); + + // This means localhost + if (name == NULL) + { + struct sockaddr_in *sin = (sockaddr_in*)malloc(sizeof(*sin)); +# ifdef AF_INET6 + struct sockaddr_in6 *sin6; +# endif + + if (hint->ai_family == AF_INET || hint->ai_family == AF_UNSPEC) + { + if (sin == NULL) + { + free(sin); + freeaddrinfo(p); + return EAI_MEMORY; + } + + // Do IPv4 first + q = (addrinfo*)malloc(sizeof(*q)); + if (q == NULL) + { + free(sin); + freeaddrinfo(p); + return EAI_MEMORY; + } + + sin->sin_family = AF_INET; +# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + sin->sin_len = sizeof(*sin); +# endif + sin->sin_port = portnum; + if (hint->ai_flags & AI_PASSIVE) + *(Q_UINT32*)&sin->sin_addr = INADDR_ANY; + else + *(Q_UINT32*)&sin->sin_addr = htonl(INADDR_LOOPBACK); + q->ai_flags = 0; + q->ai_family = AF_INET; + q->ai_socktype = hint->ai_socktype; + q->ai_protocol = protonum; + q->ai_addrlen = sizeof(*sin); + q->ai_canonname = NULL; + q->ai_addr = (sockaddr*)sin; + q->ai_next = p; + p = q; + } + +# ifdef AF_INET6 + // Try now IPv6 + + if (hint->ai_family == AF_INET6 || hint->ai_family == AF_UNSPEC) + { + sin6 = (sockaddr_in6*)malloc(sizeof(*sin6)); + q = (addrinfo*)malloc(sizeof(*q)); + if (q == NULL || sin6 == NULL) + { + free(sin6); + free(q); + freeaddrinfo(p); + return EAI_MEMORY; + } + + sin6->sin6_family = AF_INET6; +# ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + sin6->sin6_len = sizeof(*sin6); +# endif + sin6->sin6_port = portnum; + sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; + + // We don't want to use in6addr_loopback and in6addr_any + memset(&sin6->sin6_addr, 0, sizeof(sin6->sin6_addr)); + if ((hint->ai_flags & AI_PASSIVE) == 0) + ((char*)&sin6->sin6_addr)[15] = 1; + + q->ai_flags = 0; + q->ai_family = AF_INET6; + q->ai_socktype = hint->ai_socktype; + q->ai_protocol = protonum; + q->ai_addrlen = sizeof(*sin6); + q->ai_canonname = NULL; + q->ai_addr = (sockaddr*)sin6; + q->ai_next = p; + p = q; + } + +# endif // AF_INET6 + + *result = p; + return 0; // success! + } + + return inet_lookup(name, portnum, protonum, p, hint, result); +} + + +int getaddrinfo(const char *name, const char *serv, + const struct addrinfo* hint, + struct addrinfo** result) +{ + unsigned short portnum; // remember to store in network byte order + int protonum = IPPROTO_TCP; + const char *proto = "tcp"; + struct addrinfo *p = NULL; + + // Sanity checks: + if (hint == NULL || result == NULL) + return EAI_BADFLAGS; + if (hint->ai_family != AF_UNSPEC && hint->ai_family != AF_UNIX && + hint->ai_family != AF_INET +# ifdef AF_INET6 + && hint->ai_family != AF_INET6 +# endif + ) + return EAI_FAMILY; + if (hint->ai_socktype != 0 && hint->ai_socktype != SOCK_STREAM && + hint->ai_socktype != SOCK_DGRAM) + return EAI_SOCKTYPE; + + // Treat hostname of "*" as NULL, which means localhost + if (name != NULL && ((*name == '*' && name[1] == '\0') || *name == '\0')) + name = NULL; + // Treat service of "*" as NULL, which I guess means no port (0) + if (serv != NULL && ((*serv == '*' && serv[1] == '\0') || *serv == '\0')) + serv = NULL; + + if (name == NULL && serv == NULL) // what the hell do you want? + return EAI_NONAME; + + // This is just to make it easier + if (name != NULL && strcmp(name, "localhost") == 0) + name = NULL; + + // First, check for a Unix socket + // family must be either AF_UNIX or AF_UNSPEC + // either of name or serv must be set, the other must be NULL or empty + if (hint->ai_family == AF_UNIX || hint->ai_family == AF_UNSPEC) + { + if (name != NULL && serv != NULL) + { + // This is not allowed + if (hint->ai_family == AF_UNIX) + return EAI_BADFLAGS; + } + else + { + p = make_unix(name, serv); + if (p == NULL) + return EAI_MEMORY; + + p->ai_socktype = hint->ai_socktype; + // If the name/service started with a slash, then this *IS* + // only a Unix socket. Return. + if (hint->ai_family == AF_UNIX || ((name != NULL && *name == '/') || + (serv != NULL && *serv == '/'))) + { + *result = p; + return 0; // successful lookup + } + } + } + + // Lookup the service name, if required + if (serv != NULL) + { + char *tail; + struct servent *sent; + + portnum = htons((unsigned)strtoul(serv, &tail, 10)); + if (*tail != '\0') + { + // not a number. We have to do the lookup + if (hint->ai_socktype == SOCK_DGRAM) + { + proto = "udp"; + protonum = IPPROTO_UDP; + } + + sent = getservbyname(serv, proto); + if (sent == NULL) // no service? + { + if (p == NULL) + return EAI_NONAME; + else + return 0; // a Unix socket available + } + + portnum = sent->s_port; + } + } + else + portnum = 0; // no port number + + return make_inet(name, portnum, protonum, p, hint, result); +} + +void freeaddrinfo(struct addrinfo *p) +{ + dofreeaddrinfo(p); +} + +char *gai_strerror(int errorcode) +{ + static const char * const messages[] = + { + I18N_NOOP("no error"), // 0 + I18N_NOOP("address family for nodename not supported"), // EAI_ADDRFAMILY + I18N_NOOP("temporary failure in name resolution"), // EAI_AGAIN + I18N_NOOP("invalid value for 'ai_flags'"), // EAI_BADFLAGS + I18N_NOOP("non-recoverable failure in name resolution"), // EAI_FAIL + I18N_NOOP("'ai_family' not supported"), // EAI_FAMILY + I18N_NOOP("memory allocation failure"), // EAI_MEMORY + I18N_NOOP("no address associated with nodename"), // EAI_NODATA + I18N_NOOP("name or service not known"), // EAI_NONAME + I18N_NOOP("servname not supported for ai_socktype"), // EAI_SERVICE + I18N_NOOP("'ai_socktype' not supported"), // EAI_SOCKTYPE + I18N_NOOP("system error") // EAI_SYSTEM + }; + + if (errorcode > EAI_SYSTEM || errorcode < 0) + return NULL; + + static char buffer[200]; + strcpy(buffer, i18n(messages[errorcode]).local8Bit()); + return buffer; +} + +static void findport(unsigned short port, char *serv, size_t servlen, int flags) +{ + if (serv == NULL) + return; + + if ((flags & NI_NUMERICSERV) == 0) + { + struct servent *sent; + sent = getservbyport(ntohs(port), flags & NI_DGRAM ? "udp" : "tcp"); + if (sent != NULL && servlen > strlen(sent->s_name)) + { + strcpy(serv, sent->s_name); + return; + } + } + + snprintf(serv, servlen, "%u", ntohs(port)); +} + +int getnameinfo(const struct sockaddr *sa, ksocklen_t salen, + char *host, size_t hostlen, char *serv, size_t servlen, + int flags) +{ + union + { + const sockaddr *sa; + const sockaddr_un *_sun; + const sockaddr_in *sin; + const sockaddr_in6 *sin6; + } s; + + if ((host == NULL || hostlen == 0) && (serv == NULL || servlen == 0)) + return 1; + + s.sa = sa; + if (s.sa->sa_family == AF_UNIX) + { + if (salen < offsetof(struct sockaddr_un, sun_path) + strlen(s._sun->sun_path) + 1) + return 1; // invalid socket + + if (servlen && serv != NULL) + *serv = '\0'; + if (host != NULL && hostlen > strlen(s._sun->sun_path)) + strcpy(host, s._sun->sun_path); + + return 0; + } + else if (s.sa->sa_family == AF_INET) + { + if (salen < offsetof(struct sockaddr_in, sin_addr) + sizeof(s.sin->sin_addr)) + return 1; // invalid socket + + if (flags & NI_NUMERICHOST) + inet_ntop(AF_INET, &s.sin->sin_addr, host, hostlen); + else + { + // have to do lookup + struct hostent *h = gethostbyaddr((const char*)&s.sin->sin_addr, sizeof(s.sin->sin_addr), + AF_INET); + if (h == NULL && flags & NI_NAMEREQD) + return 1; + else if (h == NULL) + inet_ntop(AF_INET, &s.sin->sin_addr, host, hostlen); + else if (host != NULL && hostlen > strlen(h->h_name)) + strcpy(host, h->h_name); + else + return 1; // error + } + + findport(s.sin->sin_port, serv, servlen, flags); + } +# ifdef AF_INET6 + else if (s.sa->sa_family == AF_INET6) + { + if (salen < offsetof(struct sockaddr_in6, sin6_addr) + sizeof(s.sin6->sin6_addr)) + return 1; // invalid socket + + if (flags & NI_NUMERICHOST) + inet_ntop(AF_INET6, &s.sin6->sin6_addr, host, hostlen); + else + { + // have to do lookup + struct hostent *h = gethostbyaddr((const char*)&s.sin->sin_addr, sizeof(s.sin->sin_addr), + AF_INET6); + if (h == NULL && flags & NI_NAMEREQD) + return 1; + else if (h == NULL) + inet_ntop(AF_INET6, &s.sin6->sin6_addr, host, hostlen); + else if (host != NULL && hostlen > strlen(h->h_name)) + strcpy(host, h->h_name); + else + return 1; // error + } + + findport(s.sin6->sin6_port, serv, servlen, flags); + } +# endif // AF_INET6 + + return 1; // invalid family +} + +#endif // HAVE_GETADDRINFO + +#ifndef HAVE_INET_NTOP + +#define KRF_inet_ntop KRF_USING_OWN_INET_NTOP + +static void add_dwords(char *buf, Q_UINT16 *dw, int count) +{ + int i = 1; + sprintf(buf + strlen(buf), "%x", ntohs(dw[0])); + while (--count) + sprintf(buf + strlen(buf), ":%x", ntohs(dw[i++])); +} + +const char* inet_ntop(int af, const void *cp, char *buf, size_t len) +{ + char buf2[sizeof "1234:5678:9abc:def0:1234:5678:255.255.255.255" + 1]; + Q_UINT8 *data = (Q_UINT8*)cp; + + if (af == AF_INET) + { + sprintf(buf2, "%u.%u.%u.%u", data[0], data[1], data[2], data[3]); + + if (len > strlen(buf2)) + { + strcpy(buf, buf2); + return buf; + } + + errno = ENOSPC; + return NULL; // failed + } + +# ifdef AF_INET6 + if (af == AF_INET6) + { + Q_UINT16 *p = (Q_UINT16*)data; + Q_UINT16 *longest = NULL, *cur = NULL; + int longest_length = 0, cur_length; + int i; + + if (KDE_IN6_IS_ADDR_V4MAPPED(p) || KDE_IN6_IS_ADDR_V4COMPAT(p)) + sprintf(buf2, "::%s%u.%u.%u.%u", + KDE_IN6_IS_ADDR_V4MAPPED(p) ? "ffff:" : "", + buf[12], buf[13], buf[14], buf[15]); + else + { + // find the longest sequence of zeroes + for (i = 0; i < 8; i++) + if (cur == NULL && p[i] == 0) + { + // a zero, start the sequence + cur = p + i; + cur_length = 1; + } + else if (cur != NULL && p[i] == 0) + // part of the sequence + cur_length++; + else if (cur != NULL && p[i] != 0) + { + // end of the sequence + if (cur_length > longest_length) + { + longest_length = cur_length; + longest = cur; + } + cur = NULL; // restart sequence + } + if (cur != NULL && cur_length > longest_length) + { + longest_length = cur_length; + longest = cur; + } + + if (longest_length > 1) + { + // We have a candidate + buf2[0] = '\0'; + if (longest != p) + add_dwords(buf2, p, longest - p); + strcat(buf2, "::"); + if (longest + longest_length < p + 8) + add_dwords(buf2, longest + longest_length, 8 - (longest - p) - longest_length); + } + else + { + // Nope, no candidate + buf2[0] = '\0'; + add_dwords(buf2, p, 8); + } + } + + if (strlen(buf2) < len) + { + strcpy(buf, buf2); + return buf; + } + + errno = ENOSPC; + return NULL; + } +# endif + + errno = EAFNOSUPPORT; + return NULL; // a family we don't know about +} + +#else // HAVE_INET_NTOP + +#define KRF_inet_ntop 0 + +#endif // HAVE_INET_NTOP + +#ifndef HAVE_INET_PTON + +#define KRF_inet_pton KRF_USING_OWN_INET_PTON +int inet_pton(int af, const char *cp, void *buf) +{ + if (af == AF_INET) + { + // Piece of cake + unsigned p[4]; + unsigned char *q = (unsigned char*)buf; + if (sscanf(cp, "%u.%u.%u.%u", p, p + 1, p + 2, p + 3) != 4) + return 0; + + if (p[0] > 0xff || p[1] > 0xff || p[2] > 0xff || p[3] > 0xff) + return 0; + + q[0] = p[0]; + q[1] = p[1]; + q[2] = p[2]; + q[3] = p[3]; + + return 1; + } + +# ifdef AF_INET6 + else if (af == AF_INET6) + { + Q_UINT16 addr[8]; + const char *p = cp; + int n = 0, start = 8; + bool has_v4 = strchr(p, '.') != NULL; + + memset(addr, 0, sizeof(addr)); + + if (*p == '\0' || p[1] == '\0') + return 0; // less than 2 chars is not valid + + if (*p == ':' && p[1] == ':') + { + start = 0; + p += 2; + } + while (*p) + { + if (has_v4 && inet_pton(AF_INET, p, addr + n) != 0) + { + // successful v4 convertion + addr[n] = ntohs(addr[n]); + n++; + addr[n] = ntohs(addr[n]); + n++; + break; + } + if (sscanf(p, "%hx", addr + n++) != 1) + return 0; + + while (*p && *p != ':') + p++; + if (!*p) + break; + p++; + + if (*p == ':') // another ':'? + { + if (start != 8) + return 0; // two :: were found + start = n; + p++; + } + } + + // if start is not 8, then a "::" was found at word 'start' + // n is the number of converted words + // n == 8 means everything was converted and no moving is necessary + // n < 8 means that we have to move n - start words 8 - n words to the right + if (start == 8 && n != 8) + return 0; // bad conversion + memmove(addr + start + (8 - n), addr + start, (n - start) * sizeof(Q_UINT16)); + memset(addr + start, 0, (8 - n) * sizeof(Q_UINT16)); + + // check the byte order + // The compiler should optimise this out in big endian machines + if (htons(0x1234) != 0x1234) + for (n = 0; n < 8; n++) + addr[n] = htons(addr[n]); + + memcpy(buf, addr, sizeof(addr)); + return 1; + } +# endif + + errno = EAFNOSUPPORT; + return -1; // unknown family +} + +#else // HAVE_INET_PTON + +#define KRF_inet_pton 0 + +#endif // HAVE_INET_PTON + +#ifdef AF_INET6 +# define KRF_afinet6 KRF_KNOWS_AF_INET6 +#else +# define KRF_afinet6 0 +#endif + +namespace KDE +{ + /** @internal */ + extern const int KDE_EXPORT resolverFlags = KRF_getaddrinfo | KRF_resolver | KRF_afinet6 | KRF_inet_ntop | KRF_inet_pton; +} |