The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
getaddrinfo.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Replacement getaddrinfo functions
18 *
19 * These functions are defined and used only if the configure
20 * cannot detect the standard getaddrinfo(), freeaddrinfo(),
21 * gai_strerror() and getnameinfo(). This avoids sprinkling of ifdefs.
22 *
23 * FIXME: getaddrinfo() & getnameinfo() should
24 * return all IPv4 addresses provided by DNS lookup.
25 *
26 * @file src/lib/util/getaddrinfo.c
27 *
28 * @copyright 2016 The FreeRADIUS server project
29 */
30RCSID("$Id: a8d8852aca395f83b384899f3219afa6deedbfdf $")
31
32#include <ctype.h>
33#include <pthread.h>
34#include <sys/param.h>
35#include <sys/types.h>
36#include <sys/socket.h>
37#include <sys/errno.h>
38#include <netdb.h>
39
40#ifndef HAVE_GETNAMEINFO
41# undef LOCAL_GETHOSTBYNAMERSTYLE
42# ifndef GETHOSTBYNAMERSTYLE
43# define LOCAL_GETHOSTBYNAMERSTYLE 1
44#elif (GETHOSTBYNAMERSTYLE != SYSVSTYLE) && (GETHOSTBYNAMERSTYLE != GNUSTYLE)
45# define LOCAL_GETHOSTBYNAMERSTYLE 1
46# endif /* GETHOSTBYNAMERSTYLE */
47#endif
48
49#ifndef HAVE_GETADDRINFO
50# undef LOCAL_GETHOSTBYADDRR
51# ifndef GETHOSTBYADDRRSTYLE
52# define LOCAL_GETHOSTBYADDRR 1
53# elif (GETHOSTBYADDRRSTYLE != SYSVSTYLE) && (GETHOSTBYADDRRSTYLE != GNUSTYLE)
54# define LOCAL_GETHOSTBYADDRR 1
55# endif /* GETHOSTBYADDRRSTYLE */
56#endif
57
58/* Thread safe DNS lookups */
59/*
60 * FIXME: There are some systems that use the same hostent
61 * structure to return for gethostbyname() & gethostbyaddr(), if
62 * that is the case then use only one mutex instead of separate
63 * mutexes
64 */
65#ifdef LOCAL_GETHOSTBYNAMERSTYLE
66static int fr_hostbyname = 0;
68#endif
69
70#ifdef LOCAL_GETHOSTBYNAMERSTYLE
71static int fr_hostbyaddr = 0;
73#endif
74
75/*
76 * gethostbyaddr() & gethostbyname() return hostent structure
77 * To make these functions thread safe, we need to
78 * copy the data and not pointers
79 *
80 * struct hostent {
81 * char *h_name; * official name of host *
82 * char **h_aliases; * alias list *
83 * int h_addrtype; * host address type *
84 * int h_length; * length of address *
85 * char **h_addr_list; * list of addresses *
86 * }
87 * This struct contains 3 pointers as members.
88 * The data from these pointers is copied into a buffer.
89 * The buffer is formatted as below to store the data
90 * ---------------------------------------------------------------
91 * | h_name\0alias_array\0h_aliases\0..\0addr_array\0h_addr_list\0 |
92 * ---------------------------------------------------------------
93 */
94#if defined(LOCAL_GETHOSTBYNAMER) || defined(LOCAL_GETHOSTBYADDRR)
95static int copy_hostent(struct hostent *from, struct hostent *to, char *buffer, size_t buflen, int *error)
96{
97 int i;
98 size_t len;
99 uintptr_t mask;
100 char *ptr = buffer, *end = buffer + buflen;
101
102 *error = 0;
103 to->h_addrtype = from->h_addrtype;
104 to->h_length = from->h_length;
105 to->h_name = (char *) ptr;
106
107 /* copy hostname to buffer */
108 len = strlen(from->h_name) + 1;
109 if (len > buflen) {
110 overflow:
111 *error = ERANGE;
112 return -1;
113 }
114 memcpy(ptr, from->h_name, len);
115 ptr += len;
116
117 mask = ptr;
118 mask &= _Alignof(char **) - 1; /* 0..7 */
119 mask = _Alignof(char **) - mask; /* 8..1 */
120 mask &= _Alignof(char **) - 1; /* 0,7..1 */
121
122 if (mask > ((size_t) (end - ptr))) goto overflow;
123
124 ptr += mask;
125
126 /*
127 * Copy aliases to buffer
128 *
129 * Aliases are 'char *'
130 */
131 to->h_aliases = (char **) (uintptr_t) ptr;
132
133 for (i = 0; from->h_aliases[i] != NULL; i++) {
134 /* nothing */
135 }
136
137 len = (i + 1) * sizeof(char *);
138 if (len > (size_t) (end - ptr)) goto overflow;
139 ptr += len;
140
141 for (i = 0; from->h_aliases[i]; i++) {
142 len = strlen(from->h_aliases[i]) + 1;
143 if (len > (size_t) (end - ptr)) goto overflow;
144
145 to->h_aliases[i] = ptr;
146 memcpy(ptr, from->h_aliases[i], len);
147 ptr += len;
148 }
149 to->h_aliases[i] = NULL;
150
151 /*
152 * Copy addr_list to buffer
153 *
154 * addr_list is an array of 'char *', each of length 'h_length'
155 */
156 mask = ptr;
157 mask &= _Alignof(char **) - 1;
158 mask = _Alignof(char **) - mask;
159 mask &= _Alignof(char **) - 1;
160
161 if (mask > ((size_t) (end - ptr))) goto overflow;
162
163 ptr += mask;
164
165 to->h_addr_list = (char **) (uintptr_t) ptr;
166
167 for (i = 0; from->h_addr_list[i] != NULL; i++) {
168 /* nothing */
169 }
170
171 len = (i + 1) * sizeof(uint32_t);
172 if (len > (size_t) (end - ptr)) goto overflow;
173 ptr += len;
174
175 /*
176 * Check if there is enough room for all of the addresses.
177 */
178 len = i * from->h_length;
179 if (len > (size_t) (end - ptr)) goto overflow; /* NOT <=, as we can copy the entire list */
180
181 for (i = 0; from->h_addr_list[i] != NULL; i++) {
182 to->h_addr_list[i] = ptr;
183 memcpy(ptr, from->h_addr_list[i], from->h_length);
184 ptr += from->h_length;
185 }
186 to->h_addr_list[i] = NULL;
187
188 return *error;
189}
190#endif /* (LOCAL_GETHOSTBYNAMER == 1) || (LOCAL_GETHOSTBYADDRR == 1) */
191
192#ifdef LOCAL_GETHOSTBYNAMERSTYLE
193static struct hostent *
194gethostbyname_r(char const *hostname, struct hostent *result,
195 char *buffer, int buflen, int *error)
196{
197 struct hostent *hp;
198
199 if (fr_hostbyname == 0) {
200 pthread_mutex_init(&fr_hostbyname_mutex, NULL);
201 fr_hostbyname = 1;
202 }
203 pthread_mutex_lock(&fr_hostbyname_mutex);
204
205 hp = gethostbyname(hostname);
206 if (!hp) {
207 *error = h_errno;
208 hp = NULL;
209 } else {
210 copy_hostent(hp, result, buffer, buflen, error);
211 hp = result;
212 }
213
214 pthread_mutex_unlock(&fr_hostbyname_mutex);
215
216 return hp;
217}
218#endif /* GETHOSTBYNAMERSTYLE */
219
220
221#ifdef LOCAL_GETHOSTBYADDRR
222static struct hostent *gethostbyaddr_r(char const *addr, int len, int type, struct hostent *result,
223 char *buffer, int buflen, int *error)
224{
225 struct hostent *hp;
226
227 if (fr_hostbyaddr == 0) {
228 pthread_mutex_init(&fr_hostbyaddr_mutex, NULL);
229 fr_hostbyaddr = 1;
230 }
231 pthread_mutex_lock(&fr_hostbyaddr_mutex);
232
233 hp = gethostbyaddr(addr, len, type);
234 if (!hp) {
235 *error = h_errno;
236 hp = NULL;
237 } else {
238 copy_hostent(hp, result, buffer, buflen, error);
239 hp = result;
240 }
241
242 pthread_mutex_unlock(&fr_hostbyaddr_mutex);
243
244 return hp;
245}
246#endif /* GETHOSTBYADDRRSTYLE */
247
248/*
249 * Mar 8, 2000 by Hajimu UMEMOTO <ume@mahoroba.org>
250 *
251 * Below code is based on ssh-1.2.27-IPv6-1.5 written by
252 * KIKUCHI Takahiro <kick@kyoto.wide.ad.jp>
253 */
254
255#ifndef HAVE_GETADDRINFO
256static struct addrinfo *alloc_ai(uint16_t port, u_long addr, int socktype, int proto)
257{
258 struct addrinfo *ai;
259
260 MEM(ai = (struct addrinfo *)talloc_zero_array(NULL, uint8_t,
261 sizeof(struct addrinfo) + sizeof(struct sockaddr_in)));
262 ai->ai_addr = (struct sockaddr *)(ai + 1);
263 ai->ai_addrlen = sizeof(struct sockaddr_in);
264# ifdef HAVE_SOCKADDR_SA_LEN
265 ai->ai_addr->sa_len = sizeof(struct sockaddr_in);
266# endif
267 ai->ai_addr->sa_family = ai->ai_family = AF_INET;
268 ((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
269 ((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
270 ai->ai_socktype = socktype;
271 ai->ai_protocol = proto;
272
273 return ai;
274}
275
276char const *gai_strerror(int ecode)
277{
278 switch (ecode) {
279 case EAI_MEMORY:
280 return "memory allocation failure";
281
282 case EAI_FAMILY:
283 return "ai_family not supported";
284
285 case EAI_NONAME:
286 return "hostname nor servname provided, or not known";
287
288 case EAI_SERVICE:
289 return "servname not supported for ai_socktype";
290
291 default:
292 return "unknown error";
293 }
294}
295
296void freeaddrinfo(struct addrinfo *ai)
297{
298 struct addrinfo *next;
299
300 do {
301 next = ai->ai_next;
302 talloc_free(ai);
303 } while ((ai = next) != NULL);
304}
305
306int getaddrinfo(char const *hostname, char const *servname, struct addrinfo const *hints, struct addrinfo **res)
307{
308 struct addrinfo *cur, *prev = NULL;
309 struct hostent *hp;
310 struct hostent result;
311 struct in_addr in;
312 int i, socktype, proto;
313 uint16_t port = 0;
314 int error;
315 char buffer[2048];
316
317 if (hints && (hints->ai_family != PF_INET) && (hints->ai_family != PF_UNSPEC)) return EAI_FAMILY;
318
319 socktype = (hints && hints->ai_socktype) ? hints->ai_socktype : SOCK_STREAM;
320 if (hints && hints->ai_protocol) {
321 proto = hints->ai_protocol;
322 } else {
323 switch (socktype) {
324 case SOCK_DGRAM:
325 proto = IPPROTO_UDP;
326 break;
327 case SOCK_STREAM:
328 proto = IPPROTO_TCP;
329 break;
330 default:
331 proto = 0;
332 break;
333 }
334 }
335
336 if (servname) {
337 if (isdigit((uint8_t)*servname)) {
338 port = htons(atoi(servname));
339 } else {
340 struct servent *se;
341 char const *pe_proto;
342
343 switch (socktype) {
344 case SOCK_DGRAM:
345 pe_proto = "udp";
346 break;
347
348 case SOCK_STREAM:
349 pe_proto = "tcp";
350 break;
351
352 default:
353 pe_proto = NULL;
354 break;
355 }
356 if ((se = getservbyname(servname, pe_proto)) == NULL) return EAI_SERVICE;
357
358 port = se->s_port;
359 }
360 }
361
362 if (!hostname) {
363 if (hints && hints->ai_flags & AI_PASSIVE) {
364 *res = alloc_ai(port, htonl(0x00000000), socktype, proto);
365 } else {
366 *res = alloc_ai(port, htonl(0x7f000001), socktype, proto);
367 }
368 if (!*res) return EAI_MEMORY;
369
370 return 0;
371 }
372
373 /* Numeric IP Address */
374 if (inet_aton(hostname, &in)) {
375 *res = alloc_ai(port, in.s_addr, socktype, proto);
376 if (!*res) return EAI_MEMORY;
377
378 return 0;
379 }
380
381 if (hints && hints->ai_flags & AI_NUMERICHOST) return EAI_NONAME;
382
383 /* DNS Lookup */
384#ifdef GETHOSTBYNAMERSTYLE
385# if GETHOSTBYNAMERSTYLE == SYSVSTYLE
386 hp = gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &error);
387# elif GETHOSTBYNAMERSTYLE == GNUSTYLE
388 if (gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &hp, &error) != 0) hp = NULL;
389# else
390 hp = gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &error);
391# endif
392#else
393 hp = gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &error);
394#endif
395
396 if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) {
397 for (i = 0; hp->h_addr_list[i]; i++) {
398 if ((cur = alloc_ai(port, ((struct in_addr *)hp->h_addr_list[i])->s_addr,
399 socktype, proto)) == NULL) {
400 if (*res) freeaddrinfo(*res);
401 return EAI_MEMORY;
402 }
403
404 if (prev) {
405 prev->ai_next = cur;
406 } else {
407 *res = cur;
408 }
409 prev = cur;
410 }
411
412 if (hints && hints->ai_flags & AI_CANONNAME && *res) {
413 if (((*res)->ai_canonname = talloc_strdup(*res, hp->h_name)) == NULL) {
414 freeaddrinfo(*res);
415 return EAI_MEMORY;
416 }
417 }
418 return 0;
419 }
420 return EAI_NONAME;
421}
422#endif /* HAVE_GETADDRINFO */
423
424
425#ifndef HAVE_GETNAMEINFO
426int getnameinfo(struct sockaddr const *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen,
427 unsigned int flags)
428{
429 const struct sockaddr_in *sin = (struct sockaddr_in const *)sa;
430 struct hostent *hp;
431 struct hostent result;
432 char tmpserv[16];
433 char buffer[2048];
434 int error;
435
436 if (serv) {
437 snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port));
438 if (strlen(tmpserv) > servlen) return EAI_MEMORY;
439
440 strcpy(serv, tmpserv);
441
442 if (host) {
443 if (flags & NI_NUMERICHOST) {
444 /* No Reverse DNS lookup */
445 if (flags & NI_NAMEREQD) return EAI_NONAME;
446 if (strlen(inet_ntoa(sin->sin_addr)) >= hostlen) return EAI_MEMORY;
447
448 strcpy(host, inet_ntoa(sin->sin_addr));
449 return 0;
450 } else {
451 /* Reverse DNS lookup required */
452#ifdef GETHOSTBYADDRRSTYLE
453# if GETHOSTBYADDRRSTYLE == SYSVSTYLE
454 hp = gethostbyaddr_r((char const *)&sin->sin_addr,
455 salen, AF_INET, &result, buffer, sizeof(buffer), &error);
456# elif GETHOSTBYADDRRSTYLE == GNUSTYLE
457 if (gethostbyaddr_r((char const *)&sin->sin_addr, salen, AF_INET,
458 &result, buffer, sizeof(buffer), &hp, &error) != 0) {
459 hp = NULL;
460 }
461# else
462 hp = gethostbyaddr_r((char const *)&sin->sin_addr, salen, AF_INET,
463 &result, buffer, sizeof(buffer), &error);
464# endif
465#else
466 hp = gethostbyaddr_r((char const *)&sin->sin_addr, salen, AF_INET,
467 &result, buffer, sizeof(buffer), &error);
468#endif
469 if (hp) {
470 if (strlen(hp->h_name) >= hostlen) return EAI_MEMORY;
471
472 strcpy(host, hp->h_name);
473 return 0;
474 }
475
476 if (flags & NI_NAMEREQD) return EAI_NONAME;
477 if (strlen(inet_ntoa(sin->sin_addr)) >= hostlen) return EAI_MEMORY;
478
479 strcpy(host, inet_ntoa(sin->sin_addr));
480 return 0;
481 }
482 }
483 return 0;
484}
485#endif /* HAVE_GETNAMEINFO */
static int const char char buffer[256]
Definition acutest.h:576
strcpy(log_entry->msg, buffer)
#define RCSID(id)
Definition build.h:506
#define MEM(x)
Definition debug.h:46
static fr_slen_t in
Definition dict.h:882
int getaddrinfo(char const *hostname, char const *servname, struct addrinfo const *hints, struct addrinfo **res)
static pthread_mutex_t fr_hostbyname_mutex
Definition getaddrinfo.c:67
char const * gai_strerror(int ecode)
static int fr_hostbyname
Definition getaddrinfo.c:66
void freeaddrinfo(struct addrinfo *ai)
static int copy_hostent(struct hostent *from, struct hostent *to, char *buffer, size_t buflen, int *error)
Definition getaddrinfo.c:95
static int fr_hostbyaddr
Definition getaddrinfo.c:71
int getnameinfo(struct sockaddr const *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, unsigned int flags)
static struct addrinfo * alloc_ai(uint16_t port, u_long addr, int socktype, int proto)
static pthread_mutex_t fr_hostbyaddr_mutex
Definition getaddrinfo.c:72
static struct hostent * gethostbyaddr_r(char const *addr, int len, int type, struct hostent *result, char *buffer, int buflen, int *error)
static struct hostent * gethostbyname_r(char const *hostname, struct hostent *result, char *buffer, int buflen, int *error)
talloc_free(hp)
unsigned short uint16_t
unsigned int uint32_t
unsigned char uint8_t
int inet_aton(char const *cp, struct in_addr *inp)
Definition missing.c:96
static uint32_t mask
Definition rbmonkey.c:39
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_aka_sim_id_type_t type
#define talloc_strdup(_ctx, _str)
Definition talloc.h:142