libreplace: free() deals fine with NULL pointers
[gd/samba-autobuild/.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                 free(res->ai_canonname);
325                 free(res->ai_addr);
326                 free(res);
327         }
328 }
329
330
331 const char *rep_gai_strerror(int errcode)
332 {
333 #ifdef HAVE_HSTRERROR
334         int                     hcode;
335
336         switch (errcode)
337         {
338                 case EAI_NONAME:
339                         hcode = HOST_NOT_FOUND;
340                         break;
341                 case EAI_AGAIN:
342                         hcode = TRY_AGAIN;
343                         break;
344                 case EAI_FAIL:
345                 default:
346                         hcode = NO_RECOVERY;
347                         break;
348         }
349
350         return hstrerror(hcode);
351 #else                                                   /* !HAVE_HSTRERROR */
352
353         switch (errcode)
354         {
355                 case EAI_NONAME:
356                         return "Unknown host";
357                 case EAI_AGAIN:
358                         return "Host name lookup failure";
359 #ifdef EAI_BADFLAGS
360                 case EAI_BADFLAGS:
361                         return "Invalid argument";
362 #endif
363 #ifdef EAI_FAMILY
364                 case EAI_FAMILY:
365                         return "Address family not supported";
366 #endif
367 #ifdef EAI_MEMORY
368                 case EAI_MEMORY:
369                         return "Not enough memory";
370 #endif
371 #ifdef EAI_NODATA
372                 case EAI_NODATA:
373                         return "No host data of that type was found";
374 #endif
375 #ifdef EAI_SERVICE
376                 case EAI_SERVICE:
377                         return "Class type not found";
378 #endif
379 #ifdef EAI_SOCKTYPE
380                 case EAI_SOCKTYPE:
381                         return "Socket type not supported";
382 #endif
383                 default:
384                         return "Unknown server error";
385         }
386 #endif   /* HAVE_HSTRERROR */
387 }
388
389 static int gethostnameinfo(const struct sockaddr *sa,
390                         char *node,
391                         size_t nodelen,
392                         int flags)
393 {
394         int ret = -1;
395         char *p = NULL;
396
397         if (!(flags & NI_NUMERICHOST)) {
398                 struct hostent *hp = gethostbyaddr(
399                                 &((struct sockaddr_in *)sa)->sin_addr,
400                                 sizeof(struct in_addr),
401                                 sa->sa_family);
402                 ret = check_hostent_err(hp);
403                 if (ret == 0) {
404                         /* Name looked up successfully. */
405                         ret = snprintf(node, nodelen, "%s", hp->h_name);
406                         if (ret < 0 || (size_t)ret >= nodelen) {
407                                 return EAI_MEMORY;
408                         }
409                         if (flags & NI_NOFQDN) {
410                                 p = strchr(node,'.');
411                                 if (p) {
412                                         *p = '\0';
413                                 }
414                         }
415                         return 0;
416                 }
417
418                 if (flags & NI_NAMEREQD) {
419                         /* If we require a name and didn't get one,
420                          * automatically fail. */
421                         return ret;
422                 }
423                 /* Otherwise just fall into the numeric host code... */
424         }
425         p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
426         ret = snprintf(node, nodelen, "%s", p);
427         if (ret < 0 || (size_t)ret >= nodelen) {
428                 return EAI_MEMORY;
429         }
430         return 0;
431 }
432
433 static int getservicenameinfo(const struct sockaddr *sa,
434                         char *service,
435                         size_t servicelen,
436                         int flags)
437 {
438         int ret = -1;
439         int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
440
441         if (!(flags & NI_NUMERICSERV)) {
442                 struct servent *se = getservbyport(
443                                 port,
444                                 (flags & NI_DGRAM) ? "udp" : "tcp");
445                 if (se && se->s_name) {
446                         /* Service name looked up successfully. */
447                         ret = snprintf(service, servicelen, "%s", se->s_name);
448                         if (ret < 0 || (size_t)ret >= servicelen) {
449                                 return EAI_MEMORY;
450                         }
451                         return 0;
452                 }
453                 /* Otherwise just fall into the numeric service code... */
454         }
455         ret = snprintf(service, servicelen, "%d", port);
456         if (ret < 0 || (size_t)ret >= servicelen) {
457                 return EAI_MEMORY;
458         }
459         return 0;
460 }
461
462 /*
463  * Convert an ipv4 address to a hostname.
464  *
465  * Bugs:        - No IPv6 support.
466  */
467 int rep_getnameinfo(const struct sockaddr *sa, socklen_t salen,
468                         char *node, size_t nodelen,
469                         char *service, size_t servicelen, int flags)
470 {
471
472         /* Invalid arguments. */
473         if (sa == NULL || (node == NULL && service == NULL)) {
474                 return EAI_FAIL;
475         }
476
477         if (sa->sa_family != AF_INET) {
478                 return EAI_FAIL;
479         }
480
481         if (salen < sizeof(struct sockaddr_in)) {
482                 return EAI_FAIL;
483         }
484
485         if (node) {
486                 return gethostnameinfo(sa, node, nodelen, flags);
487         }
488
489         if (service) {
490                 return getservicenameinfo(sa, service, servicelen, flags);
491         }
492         return 0;
493 }