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