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