summaryrefslogtreecommitdiff
path: root/ares_getnameinfo.c
diff options
context:
space:
mode:
authorDominick Meglio <dcm5151@esu.edu>2005-05-16 18:06:54 +0000
committerDominick Meglio <dcm5151@esu.edu>2005-05-16 18:06:54 +0000
commitaba0b775ea30596d8666e8e403d2efb8f647358d (patch)
tree3df88f0681b1f161b06676207a0bfe8180490386 /ares_getnameinfo.c
parenta6f7820033f0528cc6b7d32e7515a7d4d520238e (diff)
downloadc-ares-aba0b775ea30596d8666e8e403d2efb8f647358d.tar.gz
c-ares-aba0b775ea30596d8666e8e403d2efb8f647358d.tar.bz2
c-ares-aba0b775ea30596d8666e8e403d2efb8f647358d.zip
Added ares_getnameinfo which mimics the getnameinfo API
Diffstat (limited to 'ares_getnameinfo.c')
-rw-r--r--ares_getnameinfo.c322
1 files changed, 322 insertions, 0 deletions
diff --git a/ares_getnameinfo.c b/ares_getnameinfo.c
new file mode 100644
index 0000000..955207a
--- /dev/null
+++ b/ares_getnameinfo.c
@@ -0,0 +1,322 @@
+/* Copyright 2005 by Dominick Meglio
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+#include "setup.h"
+#include <sys/types.h>
+
+#if defined(WIN32) && !defined(WATT32)
+#include "nameser.h"
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/nameser.h>
+#ifdef HAVE_ARPA_NAMESER_COMPAT_H
+#include <arpa/nameser_compat.h>
+#endif
+#endif
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ares.h"
+#include "ares_private.h"
+#include "ares_ipv6.h"
+#include "inet_ntop.h"
+
+#ifdef WATT32
+#undef WIN32
+#endif
+
+struct nameinfo_query {
+ ares_nameinfo_callback callback;
+ void *arg;
+ union {
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+ } addr;
+ int family;
+ int flags;
+};
+
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
+#define IPBUFSIZ 40+IF_NAMESIZE
+#else
+#define IPBUFSIZ 40
+#endif
+
+static void nameinfo_callback(void *arg, int status, struct hostent *host);
+static char *lookup_service(unsigned short port, int flags, char *buf);
+static char *append_scopeid(struct sockaddr_in6 *addr6, unsigned int scopeid, char *buf);
+static char *ares_striendstr(const char *s1, const char *s2);
+
+void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa, socklen_t salen,
+ int flags, ares_nameinfo_callback callback, void *arg)
+{
+ struct sockaddr_in *addr;
+ struct sockaddr_in6 *addr6;
+ struct nameinfo_query *niquery;
+
+ /* Verify the buffer size */
+ if (salen == sizeof(struct sockaddr_in))
+ addr = (struct sockaddr_in *)sa;
+ else if (salen == sizeof(struct sockaddr_in6))
+ addr6 = (struct sockaddr_in6 *)sa;
+ else
+ {
+ callback(arg, ARES_ENOTIMP, NULL, NULL);
+ return;
+ }
+
+ /* If neither, assume they want a host */
+ if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
+ flags |= ARES_NI_LOOKUPHOST;
+
+ /* All they want is a service, no need for DNS */
+ if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
+ {
+ char buf[33], *service;
+ unsigned int port = 0;
+
+ if (salen == sizeof(struct sockaddr_in))
+ port = addr->sin_port;
+ else
+ port = addr6->sin6_port;
+ service = lookup_service(port, flags, buf);
+ callback(arg, ARES_SUCCESS, NULL, service);
+ return;
+ }
+
+ /* They want a host lookup */
+ if ((flags & ARES_NI_LOOKUPHOST))
+ {
+ /* A numeric host can be handled without DNS */
+ if ((flags & ARES_NI_NUMERICHOST))
+ {
+ unsigned int port = 0;
+ char ipbuf[IPBUFSIZ];
+ char srvbuf[32];
+ char *service = NULL;
+ ipbuf[0] = 0;
+
+ /* Specifying not to lookup a host, but then saying a host
+ * is required has to be illegal.
+ */
+ if (flags & ARES_NI_NAMEREQD)
+ {
+ callback(arg, ARES_EBADFLAGS, NULL, NULL);
+ return;
+ }
+ if (salen == sizeof(struct sockaddr_in6))
+ {
+ ares_inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, IPBUFSIZ);
+ port = addr6->sin6_port;
+ /* If the system supports scope IDs, use it */
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
+ append_scopeid(addr6, flags, ipbuf);
+#endif
+ }
+ else
+ {
+ ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ);
+ port = addr->sin_port;
+ }
+ /* They also want a service */
+ if (flags & ARES_NI_LOOKUPSERVICE)
+ service = lookup_service(port, flags, srvbuf);
+ callback(arg, ARES_SUCCESS, ipbuf, service);
+ return;
+ }
+ /* This is where a DNS lookup becomes necessary */
+ else
+ {
+ niquery = malloc(sizeof(struct nameinfo_query));
+ if (!niquery)
+ {
+ callback(arg, ARES_ENOMEM, NULL, NULL);
+ return;
+ }
+ niquery->callback = callback;
+ niquery->arg = arg;
+ niquery->flags = flags;
+ if (sa->sa_family == AF_INET)
+ {
+ niquery->family = AF_INET;
+ memcpy(&niquery->addr.addr4, addr, sizeof(addr));
+ ares_gethostbyaddr(channel, &addr->sin_addr, sizeof(struct in_addr), AF_INET,
+ nameinfo_callback, niquery);
+ }
+ else
+ {
+ niquery->family = AF_INET6;
+ memcpy(&niquery->addr.addr6, addr6, sizeof(addr6));
+ ares_gethostbyaddr(channel, &addr6->sin6_addr, sizeof(struct in6_addr), AF_INET6,
+ nameinfo_callback, niquery);
+ }
+ }
+ }
+}
+
+static void nameinfo_callback(void *arg, int status, struct hostent *host)
+{
+ struct nameinfo_query *niquery = (struct nameinfo_query *) arg;
+ char srvbuf[33];
+ char *service = NULL;
+
+
+ if (status == ARES_SUCCESS)
+ {
+ /* They want a service too */
+ if (niquery->flags & ARES_NI_LOOKUPSERVICE)
+ {
+ if (niquery->family == AF_INET)
+ service = lookup_service(niquery->addr.addr4.sin_port, niquery->flags, srvbuf);
+ else
+ service = lookup_service(niquery->addr.addr6.sin6_port, niquery->flags, srvbuf);
+ }
+ /* NOFQDN means we have to strip off the domain name portion.
+ We do this by determining our own domain name, then searching the string
+ for this domain name and removing it.
+ */
+ if (niquery->flags & ARES_NI_NOFQDN)
+ {
+ char buf[255];
+ char *domain;
+ gethostname(buf, 255);
+ if ((domain = strchr(buf, '.')))
+ {
+ char *end = ares_striendstr(host->h_name, domain);
+ if (end)
+ *end = 0;
+ }
+ }
+ callback(niquery->arg, ARES_SUCCESS, host->h_name, service);
+ return;
+ }
+ /* We couldn't find the host, but it's OK, we can use the IP */
+ else if (status == ARES_ENOTFOUND && !(niquery->flags & ARES_NI_NAMEREQD))
+ {
+ char ipbuf[IPBUFSIZ];
+ if (niquery->family == AF_INET)
+ ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf, IPBUFSIZ);
+ else
+ {
+ ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf, IPBUFSIZ);
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
+ append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf);
+#endif
+ }
+ /* They want a service too */
+ if (niquery->flags & ARES_NI_LOOKUPSERVICE)
+ {
+ if (niquery->family == AF_INET)
+ service = lookup_service(niquery->addr.addr4.sin_port, niquery->flags, srvbuf);
+ else
+ service = lookup_service(niquery->addr.addr6.sin6_port, niquery->flags, srvbuf);
+ }
+ callback(niquery->arg, ARES_SUCCESS, ipbuf, service);
+ return;
+ }
+ callback(niquery->arg, status, NULL, NULL);
+ free(niquery);
+}
+
+static char *lookup_service(unsigned short port, int flags, char *buf)
+{
+ if (port)
+ {
+ /* Just return the port as a string */
+ if (flags & ARES_NI_NUMERICSERV)
+ sprintf(buf, "%u", ntohs(port));
+ else
+ {
+ struct servent *se;
+ char *proto;
+
+ if (flags & ARES_NI_UDP)
+ proto = "udp";
+ else if (flags & ARES_NI_SCTP)
+ proto = "sctp";
+ else if (flags & ARES_NI_DCCP)
+ proto = "dccp";
+ else
+ proto = "tcp";
+ se = getservbyport(port, proto);
+ if (se && se->s_name)
+ strcpy(buf, se->s_name);
+ else
+ sprintf(buf, "%u", ntohs(port));
+ }
+ return buf;
+ }
+ return NULL;
+}
+
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
+static char *append_scopeid(struct sockaddr_in6 *addr6, unsigned int flags, char *buf)
+{
+ char tmpbuf[IF_NAMESIZE + 1];
+
+ tmpbuf[0] = '%';
+#ifdef HAVE_IF_INDEXTONAME
+ if ((flags & ARES_NI_NUMERICSCOPE) || (!IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr)
+ && !IN6_IS_ADDR_MC_LINKLOCAL(&addr6->sin6_addr)))
+ {
+ sprintf(&tmpbuf[1], "%u", addr6->sin6_scope_id);
+ }
+ else
+ {
+ if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL)
+ sprintf(&tmpbuf[1], "%u", addr6->sin6_scope_id);
+ }
+#else
+ sprintf(&tmpbuf[1], "%u", addr6->sin6_scope_id);
+#endif
+ strcat(buf, tmpbuf);
+ return buf;
+}
+#endif
+
+/* Determines if s1 ends with the string in s2 (case-insensitive) */
+static char *ares_striendstr(const char *s1, const char *s2)
+{
+ const char *c1, *c2, *c1_begin;
+ size_t s1_len = strlen(s1), s2_len = strlen(s2);
+
+ /* If the substr is longer than the full str, it can't match */
+ if (s2_len > s1_len)
+ return NULL;
+
+ /* Jump to the end of s1 minus the length of s2 */
+ c1 = (const char *)c1_begin = s1+s1_len-s2_len;
+ c2 = s2;
+ while (c2 < s2+s2_len)
+ {
+ if (tolower(*c1) != tolower(*c2))
+ return NULL;
+ else
+ {
+ c1++;
+ c2++;
+ }
+ }
+ if (c2 == c1 == NULL)
+ return c1_begin;
+ return NULL;
+}