Protocol Independent wrapper function only for IPv4.
[freeradius.git] / src / lib / getaddrinfo.c
1 /*
2  * These functions are used to avoid sprinkling of ifdefs 
3  * all around the code.
4  */
5 /*
6  * Mar  8, 2000 by Hajimu UMEMOTO <ume@mahoroba.org>
7  *
8  * This file is based on ssh-1.2.27-IPv6-1.5 written by
9  * KIKUCHI Takahiro <kick@kyoto.wide.ad.jp>
10  */
11 /*
12  * fake library for ssh
13  *
14  * This file includes getaddrinfo(), freeaddrinfo(), gai_strerror()
15  * and getnameinfo()
16  *
17  * But these functions are not implemented correctly. The minimum subset
18  * is implemented for ssh use only. For exapmle, this routine assumes
19  * that ai_family is AF_INET. Don't use it for another purpose.
20  * 
21  * In the case not using 'configure --enable-ipv6', this getaddrinfo.c
22  * will be used if you have broken getaddrinfo or no getaddrinfo.
23  */
24
25 #include        "autoconf.h"
26
27 #include        <stdio.h>
28 #include        <string.h>
29 #include        <stdlib.h>
30 #include        <netdb.h>
31 #include        <sys/types.h>
32 #include        <sys/socket.h>
33 #include        <netinet/in.h>
34 #include        <arpa/inet.h>
35 #include        <ctype.h>
36 #include        <sys/param.h>
37
38 #include        "missing.h"
39
40 #ifndef HAVE_GETADDRINFO
41 static struct addrinfo *
42 malloc_ai(int port, u_long addr, int socktype, int proto)
43 {
44     struct addrinfo *ai;
45
46     ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) +
47                                    sizeof(struct sockaddr_in));
48     if (ai) {
49         memset(ai, 0, sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
50         ai->ai_addr = (struct sockaddr *)(ai + 1);
51         ai->ai_addrlen = sizeof(struct sockaddr_in);
52 #ifdef HAVE_SOCKADDR_SA_LEN
53         ai->ai_addr->sa_len = sizeof(struct sockaddr_in);
54 #endif
55         ai->ai_addr->sa_family = ai->ai_family = AF_INET;
56         ((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
57         ((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
58         ai->ai_socktype = socktype;
59         ai->ai_protocol = proto;
60         return ai;
61     } else {
62         return NULL;
63     }
64 }
65
66 char *
67 gai_strerror(int ecode)
68 {
69     switch (ecode) {
70     case EAI_MEMORY:
71         return "memory allocation failure.";
72     case EAI_FAMILY:
73         return "ai_family not supported.";
74     case EAI_NONAME:
75         return "hostname nor servname provided, or not known.";
76     case EAI_SERVICE:
77         return "servname not supported for ai_socktype.";
78     default:
79         return "unknown error.";
80     }
81 }
82
83 void
84 freeaddrinfo(struct addrinfo *ai)
85 {
86     struct addrinfo *next;
87
88     if (ai->ai_canonname)
89         free(ai->ai_canonname);
90     do {
91         next = ai->ai_next;
92         free(ai);
93     } while ((ai = next) != NULL);
94 }
95
96 int
97 getaddrinfo(const char *hostname, const char *servname,
98             const struct addrinfo *hints, struct addrinfo **res)
99 {
100     struct addrinfo *cur, *prev = NULL;
101     struct hostent *hp;
102     struct in_addr in;
103     int i, port = 0, socktype, proto;
104
105     if (hints && hints->ai_family != PF_INET && hints->ai_family != PF_UNSPEC)
106         return EAI_FAMILY;
107
108     socktype = (hints && hints->ai_socktype) ? hints->ai_socktype
109                                              : SOCK_STREAM;
110     if (hints && hints->ai_protocol)
111         proto = hints->ai_protocol;
112     else {
113         switch (socktype) {
114         case SOCK_DGRAM:
115             proto = IPPROTO_UDP;
116             break;
117         case SOCK_STREAM:
118             proto = IPPROTO_TCP;
119             break;
120         default:
121             proto = 0;
122             break;
123         }
124     }
125     if (servname) {
126         if (isdigit((int)*servname))
127             port = htons(atoi(servname));
128         else {
129             struct servent *se;
130             char *pe_proto;
131
132             switch (socktype) {
133             case SOCK_DGRAM:
134                 pe_proto = "udp";
135                 break;
136             case SOCK_STREAM:
137                 pe_proto = "tcp";
138                 break;
139             default:
140                 pe_proto = NULL;
141                 break;
142             }
143             if ((se = getservbyname(servname, pe_proto)) == NULL)
144                 return EAI_SERVICE;
145             port = se->s_port;
146         }
147     }
148     if (!hostname) {
149         if (hints && hints->ai_flags & AI_PASSIVE)
150             *res = malloc_ai(port, htonl(0x00000000), socktype, proto);
151         else
152             *res = malloc_ai(port, htonl(0x7f000001), socktype, proto);
153         if (*res)
154             return 0;
155         else
156             return EAI_MEMORY;
157     }
158     if (inet_aton(hostname, &in)) {
159         *res = malloc_ai(port, in.s_addr, socktype, proto);
160         if (*res)
161             return 0;
162         else
163             return EAI_MEMORY;
164     }
165     if (hints && hints->ai_flags & AI_NUMERICHOST)
166         return EAI_NONAME;
167     if ((hp = gethostbyname(hostname)) &&
168         hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) {
169         for (i = 0; hp->h_addr_list[i]; i++) {
170             if ((cur = malloc_ai(port,
171                                 ((struct in_addr *)hp->h_addr_list[i])->s_addr,
172                                 socktype, proto)) == NULL) {
173                 if (*res)
174                     freeaddrinfo(*res);
175                 return EAI_MEMORY;
176             }
177             if (prev)
178                 prev->ai_next = cur;
179             else
180                 *res = cur;
181             prev = cur;
182         }
183         if (hints && hints->ai_flags & AI_CANONNAME && *res) {
184             if (((*res)->ai_canonname = strdup(hp->h_name)) == NULL) {
185                 freeaddrinfo(*res);
186                 return EAI_MEMORY;
187             }
188         }
189         return 0;
190     }
191     return EAI_NONAME;
192 }
193 #endif /*  HAVE_GETADDRINFO */
194
195 #ifndef HAVE_GETNAMEINFO
196 int
197 getnameinfo(const struct sockaddr *sa, socklen_t salen, 
198                 char *host, size_t hostlen, 
199                 char *serv, size_t servlen, 
200                 unsigned int flags)
201 {
202     struct sockaddr_in *sin = (struct sockaddr_in *)sa;
203     struct hostent *hp;
204     char tmpserv[16];
205   
206     if (serv) {
207         snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port));
208         if (strlen(tmpserv) > servlen)
209             return EAI_MEMORY;
210         else
211             strcpy(serv, tmpserv);
212     }
213     if (host) {
214         if (flags & NI_NUMERICHOST) {
215             if (flags & NI_NAMEREQD)
216                 return EAI_NONAME;
217             if (strlen(inet_ntoa(sin->sin_addr)) >= hostlen)
218                 return EAI_MEMORY;
219             else {
220                 strcpy(host, inet_ntoa(sin->sin_addr));
221                 return 0;
222             }
223         } else {
224             hp = gethostbyaddr((char *)&sin->sin_addr,
225                                sizeof(struct in_addr), AF_INET);
226             if (hp)
227                 if (strlen(hp->h_name) >= hostlen)
228                     return EAI_MEMORY;
229                 else {
230                     strcpy(host, hp->h_name);
231                     return 0;
232                 }
233             else if (flags & NI_NAMEREQD)
234                 return EAI_NONAME;
235             else if (strlen(inet_ntoa(sin->sin_addr)) >= hostlen)
236                 return EAI_MEMORY;
237             else {
238                 strcpy(host, inet_ntoa(sin->sin_addr));
239                 return 0;
240             }
241         }
242     }
243     return 0;
244 }
245 #endif /*  HAVE_GETNAMEINFO */