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