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