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