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