The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
30 RCSID("$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
62 static int fr_hostbyname = 0;
64 #endif
65 
66 #ifdef LOCAL_GETHOSTBYNAMERSTYLE
67 static 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
92 static 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
148 static struct hostent *
149 gethostbyname_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
177 static 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
211 static 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 
231 char 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 
251 void 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 
261 int 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
381 int 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:574
strcpy(log_entry->msg, buffer)
#define RCSID(id)
Definition: build.h:444
static fr_slen_t in
Definition: dict.h:645
int getaddrinfo(char const *hostname, char const *servname, struct addrinfo const *hints, struct addrinfo **res)
Definition: getaddrinfo.c:261
static pthread_mutex_t fr_hostbyname_mutex
Definition: getaddrinfo.c:63
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)
Definition: getaddrinfo.c:251
char const * gai_strerror(int ecode)
Definition: getaddrinfo.c:231
#define BUFFER_OVERFLOW
Definition: getaddrinfo.c:91
static struct hostent * gethostbyname_r(char const *hostname, struct hostent *result, char *buffer, int buflen, int *error)
Definition: getaddrinfo.c:149
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)
Definition: getaddrinfo.c:381
static pthread_mutex_t fr_hostbyaddr_mutex
Definition: getaddrinfo.c:68
static struct addrinfo * alloc_ai(uint16_t port, u_long addr, int socktype, int proto)
Definition: getaddrinfo.c:211
static struct hostent * gethostbyaddr_r(char const *addr, int len, int type, struct hostent *result, char *buffer, int buflen, int *error)
Definition: getaddrinfo.c:177
talloc_free(reap)
unsigned short uint16_t
Definition: merged_model.c:31
unsigned char uint8_t
Definition: merged_model.c:30
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
if(!subtype_vp) goto fail
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_aka_sim_id_type_t type