Merge branch 'master' of ssh://git.samba.org/data/git/samba
[tprouty/samba.git] / lib / replace / getaddrinfo.c
1 /*
2 PostgreSQL Database Management System
3 (formerly known as Postgres, then as Postgres95)
4
5 Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
6
7 Portions Copyright (c) 1994, The Regents of the University of California
8
9 Permission to use, copy, modify, and distribute this software and its
10 documentation for any purpose, without fee, and without a written agreement
11 is hereby granted, provided that the above copyright notice and this paragraph
12 and the following two paragraphs appear in all copies.
13
14 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
15 DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
16 LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
17 EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
18 SUCH DAMAGE.
19
20 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
21 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22 AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
23 ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
24 TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25
26 */
27
28 /*-------------------------------------------------------------------------
29  *
30  * getaddrinfo.c
31  *        Support getaddrinfo() on platforms that don't have it.
32  *
33  * We also supply getnameinfo() here, assuming that the platform will have
34  * it if and only if it has getaddrinfo().      If this proves false on some
35  * platform, we'll need to split this file and provide a separate configure
36  * test for getnameinfo().
37  *
38  * Copyright (c) 2003-2007, PostgreSQL Global Development Group
39  *
40  * Copyright (C) 2007 Jeremy Allison.
41  * Modified to return multiple IPv4 addresses for Samba.
42  *
43  *-------------------------------------------------------------------------
44  */
45
46 #include "replace.h"
47 #include "system/network.h"
48
49 #ifndef SMB_MALLOC
50 #define SMB_MALLOC(s) malloc(s)
51 #endif
52
53 #ifndef SMB_STRDUP
54 #define SMB_STRDUP(s) strdup(s)
55 #endif
56
57 static int check_hostent_err(struct hostent *hp)
58 {
59         if (!hp) {
60                 switch (h_errno) {
61                         case HOST_NOT_FOUND:
62                         case NO_DATA:
63                                 return EAI_NONAME;
64                         case TRY_AGAIN:
65                                 return EAI_AGAIN;
66                         case NO_RECOVERY:
67                         default:
68                                 return EAI_FAIL;
69                 }
70         }
71         if (!hp->h_name || hp->h_addrtype != AF_INET) {
72                 return EAI_FAIL;
73         }
74         return 0;
75 }
76
77 static char *canon_name_from_hostent(struct hostent *hp,
78                                 int *perr)
79 {
80         char *ret = NULL;
81
82         *perr = check_hostent_err(hp);
83         if (*perr) {
84                 return NULL;
85         }
86         ret = SMB_STRDUP(hp->h_name);
87         if (!ret) {
88                 *perr = EAI_MEMORY;
89         }
90         return ret;
91 }
92
93 static char *get_my_canon_name(int *perr)
94 {
95         char name[HOST_NAME_MAX+1];
96
97         if (gethostname(name, HOST_NAME_MAX) == -1) {
98                 *perr = EAI_FAIL;
99                 return NULL;
100         }
101         /* Ensure null termination. */
102         name[HOST_NAME_MAX] = '\0';
103         return canon_name_from_hostent(gethostbyname(name), perr);
104 }
105
106 static char *get_canon_name_from_addr(struct in_addr ip,
107                                 int *perr)
108 {
109         return canon_name_from_hostent(
110                         gethostbyaddr(&ip, sizeof(ip), AF_INET),
111                         perr);
112 }
113
114 static struct addrinfo *alloc_entry(const struct addrinfo *hints,
115                                 struct in_addr ip,
116                                 unsigned short port)
117 {
118         struct sockaddr_in *psin = NULL;
119         struct addrinfo *ai = SMB_MALLOC(sizeof(*ai));
120
121         if (!ai) {
122                 return NULL;
123         }
124         memset(ai, '\0', sizeof(*ai));
125
126         psin = SMB_MALLOC(sizeof(*psin));
127         if (!psin) {
128                 free(ai);
129                 return NULL;
130         }
131
132         memset(psin, '\0', sizeof(*psin));
133
134         psin->sin_family = AF_INET;
135         psin->sin_port = htons(port);
136         psin->sin_addr = ip;
137
138         ai->ai_flags = 0;
139         ai->ai_family = AF_INET;
140         ai->ai_socktype = hints->ai_socktype;
141         ai->ai_protocol = hints->ai_protocol;
142         ai->ai_addrlen = sizeof(*psin);
143         ai->ai_addr = (struct sockaddr *) psin;
144         ai->ai_canonname = NULL;
145         ai->ai_next = NULL;
146
147         return ai;
148 }
149
150 /*
151  * get address info for a single ipv4 address.
152  *
153  *      Bugs:   - servname can only be a number, not text.
154  */
155
156 static int getaddr_info_single_addr(const char *service,
157                                 uint32_t addr,
158                                 const struct addrinfo *hints,
159                                 struct addrinfo **res)
160 {
161
162         struct addrinfo *ai = NULL;
163         struct in_addr ip;
164         unsigned short port = 0;
165
166         if (service) {
167                 port = (unsigned short)atoi(service);
168         }
169         ip.s_addr = htonl(addr);
170
171         ai = alloc_entry(hints, ip, port);
172         if (!ai) {
173                 return EAI_MEMORY;
174         }
175
176         /* If we're asked for the canonical name,
177          * make sure it returns correctly. */
178         if (!(hints->ai_flags & AI_NUMERICSERV) &&
179                         hints->ai_flags & AI_CANONNAME) {
180                 int err;
181                 if (addr == INADDR_LOOPBACK || addr == INADDR_ANY) {
182                         ai->ai_canonname = get_my_canon_name(&err);
183                 } else {
184                         ai->ai_canonname =
185                         get_canon_name_from_addr(ip,&err);
186                 }
187                 if (ai->ai_canonname == NULL) {
188                         freeaddrinfo(ai);
189                         return err;
190                 }
191         }
192
193         *res = ai;
194         return 0;
195 }
196
197 /*
198  * get address info for multiple ipv4 addresses.
199  *
200  *      Bugs:   - servname can only be a number, not text.
201  */
202
203 static int getaddr_info_name(const char *node,
204                                 const char *service,
205                                 const struct addrinfo *hints,
206                                 struct addrinfo **res)
207 {
208         struct addrinfo *listp = NULL, *prevp = NULL;
209         char **pptr = NULL;
210         int err;
211         struct hostent *hp = NULL;
212         unsigned short port = 0;
213
214         if (service) {
215                 port = (unsigned short)atoi(service);
216         }
217
218         hp = gethostbyname(node);
219         err = check_hostent_err(hp);
220         if (err) {
221                 return err;
222         }
223
224         for(pptr = hp->h_addr_list; *pptr; pptr++) {
225                 struct in_addr ip = *(struct in_addr *)*pptr;
226                 struct addrinfo *ai = alloc_entry(hints, ip, port);
227
228                 if (!ai) {
229                         freeaddrinfo(listp);
230                         return EAI_MEMORY;
231                 }
232
233                 if (!listp) {
234                         listp = ai;
235                         prevp = ai;
236                         ai->ai_canonname = SMB_STRDUP(hp->h_name);
237                         if (!ai->ai_canonname) {
238                                 freeaddrinfo(listp);
239                                 return EAI_MEMORY;
240                         }
241                 } else {
242                         prevp->ai_next = ai;
243                         prevp = ai;
244                 }
245         }
246         *res = listp;
247         return 0;
248 }
249
250 /*
251  * get address info for ipv4 sockets.
252  *
253  *      Bugs:   - servname can only be a number, not text.
254  */
255
256 int rep_getaddrinfo(const char *node,
257                 const char *service,
258                 const struct addrinfo * hintp,
259                 struct addrinfo ** res)
260 {
261         struct addrinfo hints;
262
263         /* Setup the hints struct. */
264         if (hintp == NULL) {
265                 memset(&hints, 0, sizeof(hints));
266                 hints.ai_family = AF_INET;
267                 hints.ai_socktype = SOCK_STREAM;
268         } else {
269                 memcpy(&hints, hintp, sizeof(hints));
270         }
271
272         if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) {
273                 return EAI_FAMILY;
274         }
275
276         if (hints.ai_socktype == 0) {
277                 hints.ai_socktype = SOCK_STREAM;
278         }
279
280         if (!node && !service) {
281                 return EAI_NONAME;
282         }
283
284         if (node) {
285                 if (node[0] == '\0') {
286                         return getaddr_info_single_addr(service,
287                                         INADDR_ANY,
288                                         &hints,
289                                         res);
290                 } else if (hints.ai_flags & AI_NUMERICHOST) {
291                         struct in_addr ip;
292                         if (!inet_aton(node, &ip)) {
293                                 return EAI_FAIL;
294                         }
295                         return getaddr_info_single_addr(service,
296                                         ntohl(ip.s_addr),
297                                         &hints,
298                                         res);
299                 } else {
300                         return getaddr_info_name(node,
301                                                 service,
302                                                 &hints,
303                                                 res);
304                 }
305         } else if (hints.ai_flags & AI_PASSIVE) {
306                 return getaddr_info_single_addr(service,
307                                         INADDR_ANY,
308                                         &hints,
309                                         res);
310         }
311         return getaddr_info_single_addr(service,
312                                         INADDR_LOOPBACK,
313                                         &hints,
314                                         res);
315 }
316
317
318 void rep_freeaddrinfo(struct addrinfo *res)
319 {
320         struct addrinfo *next = NULL;
321
322         for (;res; res = next) {
323                 next = res->ai_next;
324                 if (res->ai_canonname) {
325                         free(res->ai_canonname);
326                 }
327                 if (res->ai_addr) {
328                         free(res->ai_addr);
329                 }
330                 free(res);
331         }
332 }
333
334
335 const char *rep_gai_strerror(int errcode)
336 {
337 #ifdef HAVE_HSTRERROR
338         int                     hcode;
339
340         switch (errcode)
341         {
342                 case EAI_NONAME:
343                         hcode = HOST_NOT_FOUND;
344                         break;
345                 case EAI_AGAIN:
346                         hcode = TRY_AGAIN;
347                         break;
348                 case EAI_FAIL:
349                 default:
350                         hcode = NO_RECOVERY;
351                         break;
352         }
353
354         return hstrerror(hcode);
355 #else                                                   /* !HAVE_HSTRERROR */
356
357         switch (errcode)
358         {
359                 case EAI_NONAME:
360                         return "Unknown host";
361                 case EAI_AGAIN:
362                         return "Host name lookup failure";
363 #ifdef EAI_BADFLAGS
364                 case EAI_BADFLAGS:
365                         return "Invalid argument";
366 #endif
367 #ifdef EAI_FAMILY
368                 case EAI_FAMILY:
369                         return "Address family not supported";
370 #endif
371 #ifdef EAI_MEMORY
372                 case EAI_MEMORY:
373                         return "Not enough memory";
374 #endif
375 #ifdef EAI_NODATA
376                 case EAI_NODATA:
377                         return "No host data of that type was found";
378 #endif
379 #ifdef EAI_SERVICE
380                 case EAI_SERVICE:
381                         return "Class type not found";
382 #endif
383 #ifdef EAI_SOCKTYPE
384                 case EAI_SOCKTYPE:
385                         return "Socket type not supported";
386 #endif
387                 default:
388                         return "Unknown server error";
389         }
390 #endif   /* HAVE_HSTRERROR */
391 }
392
393 static int gethostnameinfo(const struct sockaddr *sa,
394                         char *node,
395                         size_t nodelen,
396                         int flags)
397 {
398         int ret = -1;
399         char *p = NULL;
400
401         if (!(flags & NI_NUMERICHOST)) {
402                 struct hostent *hp = gethostbyaddr(
403                                 &((struct sockaddr_in *)sa)->sin_addr,
404                                 sizeof(struct in_addr),
405                                 sa->sa_family);
406                 ret = check_hostent_err(hp);
407                 if (ret == 0) {
408                         /* Name looked up successfully. */
409                         ret = snprintf(node, nodelen, "%s", hp->h_name);
410                         if (ret < 0 || (size_t)ret >= nodelen) {
411                                 return EAI_MEMORY;
412                         }
413                         if (flags & NI_NOFQDN) {
414                                 p = strchr(node,'.');
415                                 if (p) {
416                                         *p = '\0';
417                                 }
418                         }
419                         return 0;
420                 }
421
422                 if (flags & NI_NAMEREQD) {
423                         /* If we require a name and didn't get one,
424                          * automatically fail. */
425                         return ret;
426                 }
427                 /* Otherwise just fall into the numeric host code... */
428         }
429         p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
430         ret = snprintf(node, nodelen, "%s", p);
431         if (ret < 0 || (size_t)ret >= nodelen) {
432                 return EAI_MEMORY;
433         }
434         return 0;
435 }
436
437 static int getservicenameinfo(const struct sockaddr *sa,
438                         char *service,
439                         size_t servicelen,
440                         int flags)
441 {
442         int ret = -1;
443         int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
444
445         if (!(flags & NI_NUMERICSERV)) {
446                 struct servent *se = getservbyport(
447                                 port,
448                                 (flags & NI_DGRAM) ? "udp" : "tcp");
449                 if (se && se->s_name) {
450                         /* Service name looked up successfully. */
451                         ret = snprintf(service, servicelen, "%s", se->s_name);
452                         if (ret < 0 || (size_t)ret >= servicelen) {
453                                 return EAI_MEMORY;
454                         }
455                         return 0;
456                 }
457                 /* Otherwise just fall into the numeric service code... */
458         }
459         ret = snprintf(service, servicelen, "%d", port);
460         if (ret < 0 || (size_t)ret >= servicelen) {
461                 return EAI_MEMORY;
462         }
463         return 0;
464 }
465
466 /*
467  * Convert an ipv4 address to a hostname.
468  *
469  * Bugs:        - No IPv6 support.
470  */
471 int rep_getnameinfo(const struct sockaddr *sa, socklen_t salen,
472                         char *node, size_t nodelen,
473                         char *service, size_t servicelen, int flags)
474 {
475
476         /* Invalid arguments. */
477         if (sa == NULL || (node == NULL && service == NULL)) {
478                 return EAI_FAIL;
479         }
480
481         if (sa->sa_family != AF_INET) {
482                 return EAI_FAIL;
483         }
484
485         if (salen < sizeof(struct sockaddr_in)) {
486                 return EAI_FAIL;
487         }
488
489         if (node) {
490                 return gethostnameinfo(sa, node, nodelen, flags);
491         }
492
493         if (service) {
494                 return getservicenameinfo(sa, service, servicelen, flags);
495         }
496         return 0;
497 }