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 |
---|
33 | int 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 | */ |
---|
51 | static int |
---|
52 | try_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 | */ |
---|
70 | static int |
---|
71 | lookup_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 | */ |
---|
115 | static int |
---|
116 | lookup_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 | */ |
---|
144 | int |
---|
145 | getnameinfo(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 | } |
---|