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: 56294125bc1d3b9b148cef87e9469f87973f19bd $")
31
32#include <ctype.h>
33#include <pthread.h>
34#include <sys/param.h>
35
36#ifndef HAVE_GETNAMEINFO
37# undef LOCAL_GETHOSTBYNAMERSTYLE
38# ifndef GETHOSTBYNAMERSTYLE
39# define LOCAL_GETHOSTBYNAMERSTYLE 1
40#elif (GETHOSTBYNAMERSTYLE != SYSVSTYLE) && (GETHOSTBYNAMERSTYLE != GNUSTYLE)
41# define LOCAL_GETHOSTBYNAMERSTYLE 1
42# endif /* GETHOSTBYNAMERSTYLE */
43#endif
44
45#ifndef HAVE_GETADDRINFO
46# undef LOCAL_GETHOSTBYADDRR
47# ifndef GETHOSTBYADDRRSTYLE
48# define LOCAL_GETHOSTBYADDRR 1
49# elif (GETHOSTBYADDRRSTYLE != SYSVSTYLE) && (GETHOSTBYADDRRSTYLE != GNUSTYLE)
50# define LOCAL_GETHOSTBYADDRR 1
51# endif /* GETHOSTBYADDRRSTYLE */
52#endif
53
54/* Thread safe DNS lookups */
55/*
56 * FIXME: There are some systems that use the same hostent
57 * structure to return for gethostbyname() & gethostbyaddr(), if
58 * that is the case then use only one mutex instead of separate
59 * mutexes
60 */
61#ifdef LOCAL_GETHOSTBYNAMERSTYLE
62static int fr_hostbyname = 0;
64#endif
65
66#ifdef LOCAL_GETHOSTBYNAMERSTYLE
67static int fr_hostbyaddr = 0;
69#endif
70
71/*
72 * gethostbyaddr() & gethostbyname() return hostent structure
73 * To make these functions thread safe, we need to
74 * copy the data and not pointers
75 *
76 * struct hostent {
77 * char *h_name; * official name of host *
78 * char **h_aliases; * alias list *
79 * int h_addrtype; * host address type *
80 * int h_length; * length of address *
81 * char **h_addr_list; * list of addresses *
82 * }
83 * This struct contains 3 pointers as members.
84 * The data from these pointers is copied into a buffer.
85 * The buffer is formatted as below to store the data
86 * ---------------------------------------------------------------
87 * | h_name\0alias_array\0h_aliases\0..\0addr_array\0h_addr_list\0 |
88 * ---------------------------------------------------------------
89 */
90#if defined(LOCAL_GETHOSTBYNAMER) || defined(LOCAL_GETHOSTBYADDRR)
91# define BUFFER_OVERFLOW 255
92static int copy_hostent(struct hostent *from, struct hostent *to, char *buffer, int buflen, int *error)
93{
94 int i, len;
95 char *ptr = buffer;
96
97 *error = 0;
98 to->h_addrtype = from->h_addrtype;
99 to->h_length = from->h_length;
100 to->h_name = (char *)ptr;
101
102 /* copy hostname to buffer */
103 len = strlen(from->h_name) + 1;
104 strcpy(ptr, from->h_name);
105 ptr += len;
106
107 /* copy aliases to buffer */
108 to->h_aliases = (char**)ptr;
109 for (i = 0; from->h_aliases[i]; i++);
110 ptr += (i+1) * sizeof(char *);
111
112 for (i = 0; from->h_aliases[i]; i++) {
113 len = strlen(from->h_aliases[i])+1;
114 if ((ptr-buffer) + len < buflen) {
115 to->h_aliases[i] = ptr;
116 strcpy(ptr, from->h_aliases[i]);
117 ptr += len;
118 } else {
119 *error = BUFFER_OVERFLOW;
120 return *error;
121 }
122 }
123 to->h_aliases[i] = NULL;
124
125 /* copy addr_list to buffer */
126 to->h_addr_list = (char**)ptr;
127 for (i = 0; (int *)from->h_addr_list[i] != 0; i++);
128 ptr += (i + 1) * sizeof(int *);
129
130 for (i = 0; (int *)from->h_addr_list[i] != 0; i++) {
131 len = sizeof(int);
132
133 if ((ptr-buffer)+len < buflen) {
134 to->h_addr_list[i] = ptr;
135 memcpy(ptr, from->h_addr_list[i], len);
136 ptr += len;
137 } else {
138 *error = BUFFER_OVERFLOW;
139 return *error;
140 }
141 }
142 to->h_addr_list[i] = 0;
143 return *error;
144}
145#endif /* (LOCAL_GETHOSTBYNAMER == 1) || (LOCAL_GETHOSTBYADDRR == 1) */
146
147#ifdef LOCAL_GETHOSTBYNAMERSTYLE
148static struct hostent *
149gethostbyname_r(char const *hostname, struct hostent *result,
150 char *buffer, int buflen, int *error)
151{
152 struct hostent *hp;
153
154 if (fr_hostbyname == 0) {
155 pthread_mutex_init(&fr_hostbyname_mutex, NULL);
156 fr_hostbyname = 1;
157 }
158 pthread_mutex_lock(&fr_hostbyname_mutex);
159
160 hp = gethostbyname(hostname);
161 if ((!hp) || (hp->h_addrtype != AF_INET) || (hp->h_length != 4)) {
162 *error = h_errno;
163 hp = NULL;
164 } else {
165 copy_hostent(hp, result, buffer, buflen, error);
166 hp = result;
167 }
168
169 pthread_mutex_unlock(&fr_hostbyname_mutex);
170
171 return hp;
172}
173#endif /* GETHOSTBYNAMERSTYLE */
174
175
176#ifdef LOCAL_GETHOSTBYADDRR
177static struct hostent *gethostbyaddr_r(char const *addr, int len, int type, struct hostent *result,
178 char *buffer, int buflen, int *error)
179{
180 struct hostent *hp;
181
182 if (fr_hostbyaddr == 0) {
183 pthread_mutex_init(&fr_hostbyaddr_mutex, NULL);
184 fr_hostbyaddr = 1;
185 }
186 pthread_mutex_lock(&fr_hostbyaddr_mutex);
187
188 hp = gethostbyaddr(addr, len, type);
189 if ((!hp) || (hp->h_addrtype != AF_INET) || (hp->h_length != 4)) {
190 *error = h_errno;
191 hp = NULL;
192 } else {
193 copy_hostent(hp, result, buffer, buflen, error);
194 hp = result;
195 }
196
197 pthread_mutex_unlock(&fr_hostbyaddr_mutex);
198
199 return hp;
200}
201#endif /* GETHOSTBYADDRRSTYLE */
202
203/*
204 * Mar 8, 2000 by Hajimu UMEMOTO <ume@mahoroba.org>
205 *
206 * Below code is based on ssh-1.2.27-IPv6-1.5 written by
207 * KIKUCHI Takahiro <kick@kyoto.wide.ad.jp>
208 */
209
210#ifndef HAVE_GETADDRINFO
211static struct addrinfo *alloc_ai(uint16_t port, u_long addr, int socktype, int proto)
212{
213 struct addrinfo *ai;
214
215 MEM(ai = (struct addrinfo *)talloc_zero_array(NULL, uint8_t,
216 sizeof(struct addrinfo) + sizeof(struct sockaddr_in)));
217 ai->ai_addr = (struct sockaddr *)(ai + 1);
218 ai->ai_addrlen = sizeof(struct sockaddr_in);
219# ifdef HAVE_SOCKADDR_SA_LEN
220 ai->ai_addr->sa_len = sizeof(struct sockaddr_in);
221# endif
222 ai->ai_addr->sa_family = ai->ai_family = AF_INET;
223 ((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
224 ((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
225 ai->ai_socktype = socktype;
226 ai->ai_protocol = proto;
227
228 return ai;
229}
230
231char const *gai_strerror(int ecode)
232{
233 switch (ecode) {
234 case EAI_MEMORY:
235 return "memory allocation failure";
236
237 case EAI_FAMILY:
238 return "ai_family not supported";
239
240 case EAI_NONAME:
241 return "hostname nor servname provided, or not known";
242
243 case EAI_SERVICE:
244 return "servname not supported for ai_socktype";
245
246 default:
247 return "unknown error";
248 }
249}
250
251void freeaddrinfo(struct addrinfo *ai)
252{
253 struct addrinfo *next;
254
255 do {
256 next = ai->ai_next;
257 talloc_free(ai);
258 } while ((ai = next) != NULL);
259}
260
261int getaddrinfo(char const *hostname, char const *servname, struct addrinfo const *hints, struct addrinfo **res)
262{
263 struct addrinfo *cur, *prev = NULL;
264 struct hostent *hp;
265 struct hostent result;
266 struct in_addr in;
267 int i, socktype, proto;
268 uint16_t port = 0;
269 int error;
270 char buffer[2048];
271
272 if (hints && (hints->ai_family != PF_INET) && (hints->ai_family != PF_UNSPEC)) return EAI_FAMILY;
273
274 socktype = (hints && hints->ai_socktype) ? hints->ai_socktype : SOCK_STREAM;
275 if (hints && hints->ai_protocol) {
276 proto = hints->ai_protocol;
277 } else {
278 switch (socktype) {
279 case SOCK_DGRAM:
280 proto = IPPROTO_UDP;
281 break;
282 case SOCK_STREAM:
283 proto = IPPROTO_TCP;
284 break;
285 default:
286 proto = 0;
287 break;
288 }
289 }
290
291 if (servname) {
292 if (isdigit((uint8_t)*servname)) {
293 port = htons(atoi(servname));
294 } else {
295 struct servent *se;
296 char const *pe_proto;
297
298 switch (socktype) {
299 case SOCK_DGRAM:
300 pe_proto = "udp";
301 break;
302
303 case SOCK_STREAM:
304 pe_proto = "tcp";
305 break;
306
307 default:
308 pe_proto = NULL;
309 break;
310 }
311 if ((se = getservbyname(servname, pe_proto)) == NULL) return EAI_SERVICE;
312
313 port = se->s_port;
314 }
315 }
316
317 if (!hostname) {
318 if (hints && hints->ai_flags & AI_PASSIVE) {
319 *res = alloc_ai(port, htonl(0x00000000), socktype, proto);
320 } else {
321 *res = alloc_ai(port, htonl(0x7f000001), socktype, proto);
322 }
323 if (!*res) return EAI_MEMORY;
324
325 return 0;
326 }
327
328 /* Numeric IP Address */
329 if (inet_aton(hostname, &in)) {
330 *res = alloc_ai(port, in.s_addr, socktype, proto);
331 if (!*res) return EAI_MEMORY;
332
333 return 0;
334 }
335
336 if (hints && hints->ai_flags & AI_NUMERICHOST) return EAI_NONAME;
337
338 /* DNS Lookup */
339#ifdef GETHOSTBYNAMERSTYLE
340# if GETHOSTBYNAMERSTYLE == SYSVSTYLE
341 hp = gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &error);
342# elif GETHOSTBYNAMERSTYLE == GNUSTYLE
343 if (gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &hp, &error) != 0) hp = NULL;
344# else
345 hp = gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &error);
346# endif
347#else
348 hp = gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &error);
349#endif
350
351 if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) {
352 for (i = 0; hp->h_addr_list[i]; i++) {
353 if ((cur = alloc_ai(port, ((struct in_addr *)hp->h_addr_list[i])->s_addr,
354 socktype, proto)) == NULL) {
355 if (*res) freeaddrinfo(*res);
356 return EAI_MEMORY;
357 }
358
359 if (prev) {
360 prev->ai_next = cur;
361 } else {
362 *res = cur;
363 }
364 prev = cur;
365 }
366
367 if (hints && hints->ai_flags & AI_CANONNAME && *res) {
368 if (((*res)->ai_canonname = talloc_strdup(*res, hp->h_name)) == NULL) {
369 freeaddrinfo(*res);
370 return EAI_MEMORY;
371 }
372 }
373 return 0;
374 }
375 return EAI_NONAME;
376}
377#endif /* HAVE_GETADDRINFO */
378
379
380#ifndef HAVE_GETNAMEINFO
381int getnameinfo(struct sockaddr const *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen,
382 unsigned int flags)
383{
384 const struct sockaddr_in *sin = (struct sockaddr_in const *)sa;
385 struct hostent *hp;
386 struct hostent result;
387 char tmpserv[16];
388 char buffer[2048];
389 int error;
390
391 if (serv) {
392 snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port));
393 if (strlen(tmpserv) > servlen) return EAI_MEMORY;
394
395 strcpy(serv, tmpserv);
396
397 if (host) {
398 if (flags & NI_NUMERICHOST) {
399 /* No Reverse DNS lookup */
400 if (flags & NI_NAMEREQD) return EAI_NONAME;
401 if (strlen(inet_ntoa(sin->sin_addr)) >= hostlen) return EAI_MEMORY;
402
403 strcpy(host, inet_ntoa(sin->sin_addr));
404 return 0;
405 } else {
406 /* Reverse DNS lookup required */
407#ifdef GETHOSTBYADDRRSTYLE
408# if GETHOSTBYADDRRSTYLE == SYSVSTYLE
409 hp = gethostbyaddr_r((char const *)&sin->sin_addr,
410 salen, AF_INET, &result, buffer, sizeof(buffer), &error);
411# elif GETHOSTBYADDRRSTYLE == GNUSTYLE
412 if (gethostbyaddr_r((char const *)&sin->sin_addr, salen, AF_INET,
413 &result, buffer, sizeof(buffer), &hp, &error) != 0) {
414 hp = NULL;
415 }
416# else
417 hp = gethostbyaddr_r((char const *)&sin->sin_addr, salen, AF_INET,
418 &result, buffer, sizeof(buffer), &error);
419# endif
420#else
421 hp = gethostbyaddr_r((char const *)&sin->sin_addr, salen, AF_INET,
422 &result, buffer, sizeof(buffer), &error);
423#endif
424 if (hp) {
425 if (strlen(hp->h_name) >= hostlen) return EAI_MEMORY;
426
427 strcpy(host, hp->h_name);
428 return 0;
429 }
430
431 if (flags & NI_NAMEREQD) return EAI_NONAME;
432 if (strlen(inet_ntoa(sin->sin_addr)) >= hostlen) return EAI_MEMORY;
433
434 strcpy(host, inet_ntoa(sin->sin_addr));
435 return 0;
436 }
437 }
438 return 0;
439}
440#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:483
#define MEM(x)
Definition debug.h:36
static fr_slen_t in
Definition dict.h:824
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:63
char const * gai_strerror(int ecode)
static int fr_hostbyname
Definition getaddrinfo.c:62
static int copy_hostent(struct hostent *from, struct hostent *to, char *buffer, int buflen, int *error)
Definition getaddrinfo.c:92
void freeaddrinfo(struct addrinfo *ai)
#define BUFFER_OVERFLOW
Definition getaddrinfo.c:91
static int fr_hostbyaddr
Definition getaddrinfo.c:67
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:68
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(reap)
unsigned short uint16_t
unsigned char uint8_t
int inet_aton(char const *cp, struct in_addr *inp)
Definition missing.c:97
static char const * hostname(char *buf, size_t buflen, uint32_t ipaddr)
Definition radwho.c:133
static char const * proto(int id, int porttype)
Definition radwho.c:85
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_aka_sim_id_type_t type