You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/kdecore/netsupp.cpp

1238 lines
30 KiB

/*
* 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;
}