1 /* $OpenBSD: getnameinfo.c,v 1.11 2022/12/27 17:10:06 jmc Exp $ */
2 /*
3 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <net/if.h>
21 #include <arpa/inet.h>
22 #include <netinet/in.h>
23 #include <netdb.h>
24
25 #include <asr.h>
26 #include <errno.h>
27 #include <resolv.h>
28 #include <string.h>
29
30 static size_t asr_print_addr(const struct sockaddr *, char *, size_t);
31 static size_t asr_print_port(const struct sockaddr *, const char *, char *, size_t);
32
33 #define SA_IN(sa) ((struct sockaddr_in*)(sa))
34 #define SA_IN6(sa) ((struct sockaddr_in6*)(sa))
35
36 /*
37 * Print the textual representation (as given by inet_ntop(3)) of the address
38 * set in "sa".
39 *
40 * Return the total length of the string it tried to create or 0 if an error
41 * occurred, in which case errno is set. On success, the constructed string
42 * is guaranteed to be NUL-terminated. Overflow must be detected by checking
43 * the returned size against buflen.
44 *
45 */
46 static size_t
asr_print_addr(const struct sockaddr * sa,char * buf,size_t buflen)47 asr_print_addr(const struct sockaddr *sa, char *buf, size_t buflen)
48 {
49 unsigned int ifidx;
50 char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
51 char scope[IF_NAMESIZE + 1], *ifname;
52 const void *addr;
53 size_t s;
54
55 switch(sa->sa_family) {
56 case AF_INET:
57 addr = &SA_IN(sa)->sin_addr;
58 break;
59 case AF_INET6:
60 addr = &SA_IN6(sa)->sin6_addr;
61 break;
62 default:
63 errno = EINVAL;
64 return (0);
65 }
66
67 if (inet_ntop(sa->sa_family, addr, tmp, sizeof(tmp)) == NULL)
68 return (0); /* errno set */
69
70 s = strlcpy(buf, tmp, buflen);
71
72 if (sa->sa_family == AF_INET6 && SA_IN6(sa)->sin6_scope_id) {
73
74 scope[0] = SCOPE_DELIMITER;
75 scope[1] = '\0';
76
77 ifidx = SA_IN6(sa)->sin6_scope_id;
78 ifname = NULL;
79
80 if (IN6_IS_ADDR_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) ||
81 IN6_IS_ADDR_MC_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) ||
82 IN6_IS_ADDR_MC_INTFACELOCAL(&(SA_IN6(sa)->sin6_addr)))
83 ifname = if_indextoname(ifidx, scope + 1);
84
85 if (ifname == NULL)
86 (void)snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx);
87
88 if (s < buflen)
89 (void)strlcat(buf, scope, buflen);
90
91 s += strlen(scope);
92 }
93
94 return (s);
95 }
96
97 /*
98 * Print the textual representation of the port set on "sa".
99 *
100 * If proto is not NULL, it is used as parameter to "getservbyport_r(3)" to
101 * return a service name. If it's not set, or if no matching service is found,
102 * it prints the portno.
103 *
104 * Return the total length of the string it tried to create or 0 if an error
105 * occurred, in which case errno is set. On success, the constructed string
106 * is guaranteed to be NUL-terminated. Overflow must be detected by checking
107 * the returned size against buflen.
108 */
109 static size_t
asr_print_port(const struct sockaddr * sa,const char * proto,char * buf,size_t buflen)110 asr_print_port(const struct sockaddr *sa, const char *proto, char *buf, size_t buflen)
111 {
112 struct servent s;
113 struct servent_data sd;
114 int port, r, saved_errno;
115 size_t n;
116
117 switch(sa->sa_family) {
118 case AF_INET:
119 port = SA_IN(sa)->sin_port;
120 break;
121 case AF_INET6:
122 port = SA_IN6(sa)->sin6_port;
123 break;
124 default:
125 errno = EINVAL;
126 return (0);
127 }
128
129 if (proto) {
130 memset(&sd, 0, sizeof (sd));
131 saved_errno = errno;
132 r = getservbyport_r(port, proto, &s, &sd);
133 if (r == 0)
134 n = strlcpy(buf, s.s_name, buflen);
135 endservent_r(&sd);
136 errno = saved_errno;
137 if (r == 0)
138 return (n);
139 }
140
141 r = snprintf(buf, buflen, "%u", ntohs(port));
142 if (r < 0 || r >= buflen) /* Actually, this can not happen */
143 return (0);
144
145 return (r);
146 }
147
148 int
getnameinfo(const struct sockaddr * sa,socklen_t salen,char * host,size_t hostlen,char * serv,size_t servlen,int flags)149 getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
150 size_t hostlen, char *serv, size_t servlen, int flags)
151 {
152 struct asr_query *as;
153 struct asr_result ar;
154 int saved_errno = errno;
155 const char *proto;
156 size_t r;
157
158 /*
159 * Take a shortcut if we don't care about hostname,
160 * or if NI_NUMERICHOST is set.
161 */
162 if (host == NULL || hostlen == 0 ||
163 (host && hostlen && (flags & NI_NUMERICHOST))) {
164 if (host) {
165 r = asr_print_addr(sa, host, hostlen);
166 if (r == 0)
167 return (EAI_SYSTEM); /* errno set */
168 if (r >= hostlen)
169 return (EAI_OVERFLOW);
170 }
171
172 if (serv && servlen) {
173 if (flags & NI_NUMERICSERV)
174 proto = NULL;
175 else
176 proto = (flags & NI_DGRAM) ? "udp" : "tcp";
177 r = asr_print_port(sa, proto, serv, servlen);
178 if (r == 0)
179 return (EAI_SYSTEM); /* errno set */
180 if (r >= servlen)
181 return (EAI_OVERFLOW);
182 }
183
184 errno = saved_errno;
185 return (0);
186 }
187
188 res_init();
189
190 as = getnameinfo_async(sa, salen, host, hostlen, serv, servlen, flags,
191 NULL);
192 if (as == NULL) {
193 if (errno == ENOMEM) {
194 errno = saved_errno;
195 return (EAI_MEMORY);
196 }
197 return (EAI_SYSTEM);
198 }
199
200 asr_run_sync(as, &ar);
201 if (ar.ar_gai_errno == EAI_SYSTEM)
202 errno = ar.ar_errno;
203
204 return (ar.ar_gai_errno);
205 }
206 DEF_WEAK(getnameinfo);
207