source: web/old/remctl-2.14/portable/getnameinfo.c @ f6f3e91

web
Last change on this file since f6f3e91 was f6f3e91, checked in by Jessica B. Hamrick <jhamrick@…>, 15 years ago

Preserve directory hierarchy (not sure what happened to it)

  • Property mode set to 100644
File size: 5.0 KB
Line 
1/*
2 * Replacement for a missing getnameinfo.
3 *
4 * This is an implementation of getnameinfo for systems that don't have one so
5 * that networking code can use a consistant interface without #ifdef.  It is
6 * a fairly minimal implementation, with the following limitations:
7 *
8 *   - IPv4 support only.  IPv6 is not supported.
9 *   - NI_NOFQDN is ignored.
10 *   - Not thread-safe due to gethostbyaddr, getservbyport, and inet_ntoa.
11 *
12 * The last two issues could probably be easily remedied, but haven't been
13 * needed so far.  Adding IPv6 support isn't worth it; systems with IPv6
14 * support should already support getnameinfo natively.
15 *
16 * Written by Russ Allbery <rra@stanford.edu>
17 * This work is hereby placed in the public domain by its author.
18 */
19
20#include <config.h>
21#include <portable/system.h>
22#include <portable/socket.h>
23
24#include <errno.h>
25
26/*
27 * If we're running the test suite, rename inet_ntoa to avoid conflicts with
28 * the system version.  Note that we don't rename the structures and
29 * constants, but that should be okay (except possibly for gai_strerror).
30 */
31#if TESTING
32# define getnameinfo test_getnameinfo
33int test_getnameinfo(const struct sockaddr *, socklen_t, char *, socklen_t,
34                     char *, socklen_t, int);
35
36/* Linux doesn't provide EAI_OVERFLOW, so make up our own for testing. */
37# ifndef EAI_OVERFLOW
38#  define EAI_OVERFLOW 10
39# endif
40#endif
41
42/* Used for unused parameters to silence gcc warnings. */
43#define UNUSED  __attribute__((__unused__))
44
45
46/*
47 * Check to see if a name is fully qualified by seeing if it contains a
48 * period.  If it does, try to copy it into the provided node buffer and set
49 * status accordingly, returning true.  If not, return false.
50 */
51static int
52try_name(const char *name, char *node, socklen_t nodelen, int *status)
53{
54    if (strchr(name, '.') == NULL)
55        return 0;
56    if (strlen(name) > nodelen - 1)
57        *status = EAI_OVERFLOW;
58    else {
59        strlcpy(node, name, nodelen);
60        *status = 0;
61    }
62    return 1;
63}
64
65
66/*
67 * Look up an address (or convert it to ASCII form) and put it in the provided
68 * buffer, depending on what is requested by flags.
69 */
70static int
71lookup_name(const struct in_addr *addr, char *node, socklen_t nodelen,
72            int flags)
73{
74    struct hostent *host;
75    char **alias;
76    int status;
77    char *name;
78
79    /* Do the name lookup first unless told not to. */
80    if (!(flags & NI_NUMERICHOST)) {
81        host = gethostbyaddr((const void *) addr, sizeof(struct in_addr),
82                             AF_INET);
83        if (host == NULL) {
84            if (flags & NI_NAMEREQD)
85                return EAI_NONAME;
86        } else {
87            if (try_name(host->h_name, node, nodelen, &status))
88                return status;
89            for (alias = host->h_aliases; *alias != NULL; alias++)
90                if (try_name(*alias, node, nodelen, &status))
91                    return status;
92        }
93
94        /*
95         * We found some results, but none of them were fully-qualified, so
96         * act as if we found nothing and either fail or fall through.
97         */
98        if (flags & NI_NAMEREQD)
99            return EAI_NONAME;
100    }
101
102    /* Just convert the address to ASCII. */
103    name = inet_ntoa(*addr);
104    if (strlen(name) > nodelen - 1)
105        return EAI_OVERFLOW;
106    strlcpy(node, name, nodelen);
107    return 0;
108}
109
110
111/*
112 * Look up a service (or convert it to ASCII form) and put it in the provided
113 * buffer, depending on what is requested by flags.
114 */
115static int
116lookup_service(unsigned short port, char *service, socklen_t servicelen,
117               int flags)
118{
119    struct servent *srv;
120    const char *protocol;
121
122    /* Do the name lookup first unless told not to. */
123    if (!(flags & NI_NUMERICSERV)) {
124        protocol = (flags & NI_DGRAM) ? "udp" : "tcp";
125        srv = getservbyport(htons(port), protocol);
126        if (srv != NULL) {
127            if (strlen(srv->s_name) > servicelen - 1)
128                return EAI_OVERFLOW;
129            strlcpy(service, srv->s_name, servicelen);
130            return 0;
131        }
132    }
133
134    /* Just convert the port number to ASCII. */
135    if ((socklen_t) snprintf(service, servicelen, "%hu", port) > servicelen)
136        return EAI_OVERFLOW;
137    return 0;
138}
139
140
141/*
142 * The getnameinfo implementation.
143 */
144int
145getnameinfo(const struct sockaddr *sa, socklen_t salen UNUSED, char *node,
146            socklen_t nodelen, char *service, socklen_t servicelen, int flags)
147{
148    const struct sockaddr_in *sin;
149    int status;
150    unsigned short port;
151
152    if ((node == NULL || nodelen <= 0) && (service == NULL || servicelen <= 0))
153        return EAI_NONAME;
154
155    /* We only support AF_INET. */
156    if (sa->sa_family != AF_INET)
157        return EAI_FAMILY;
158    sin = (const struct sockaddr_in *) sa;
159
160    /* Name lookup. */
161    if (node != NULL && nodelen > 0) {
162        status = lookup_name(&sin->sin_addr, node, nodelen, flags);
163        if (status != 0)
164            return status;
165    }
166
167    /* Service lookup. */
168    if (service != NULL && servicelen > 0) {
169        port = ntohs(sin->sin_port);
170        return lookup_service(port, service, servicelen, flags);
171    } else
172        return 0;
173}
Note: See TracBrowser for help on using the repository browser.