Fixed our supplied getnameinfo()'s ability to do a reverse lookup,
[rsync/rsync.git] / lib / 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 "rsync.h"
47
48 #ifndef SMB_MALLOC
49 #define SMB_MALLOC(s) malloc(s)
50 #endif
51
52 #ifndef SMB_STRDUP
53 #define SMB_STRDUP(s) strdup(s)
54 #endif
55
56 #ifndef HOST_NAME_MAX
57 #define HOST_NAME_MAX 255
58 #endif
59
60 static int check_hostent_err(struct hostent *hp)
61 {
62 #ifndef INET6
63         extern int h_errno;
64 #endif
65         if (!hp) {
66                 switch (h_errno) {
67                         case HOST_NOT_FOUND:
68                         case NO_DATA:
69                                 return EAI_NONAME;
70                         case TRY_AGAIN:
71                                 return EAI_AGAIN;
72                         case NO_RECOVERY:
73                         default:
74                                 return EAI_FAIL;
75                 }
76         }
77         if (!hp->h_name || hp->h_addrtype != AF_INET) {
78                 return EAI_FAIL;
79         }
80         return 0;
81 }
82
83 static char *canon_name_from_hostent(struct hostent *hp,
84                                 int *perr)
85 {
86         char *ret = NULL;
87
88         *perr = check_hostent_err(hp);
89         if (*perr) {
90                 return NULL;
91         }
92         ret = SMB_STRDUP(hp->h_name);
93         if (!ret) {
94                 *perr = EAI_MEMORY;
95         }
96         return ret;
97 }
98
99 static char *get_my_canon_name(int *perr)
100 {
101         char name[HOST_NAME_MAX+1];
102
103         if (gethostname(name, HOST_NAME_MAX) == -1) {
104                 *perr = EAI_FAIL;
105                 return NULL;
106         }
107         /* Ensure null termination. */
108         name[HOST_NAME_MAX] = '\0';
109         return canon_name_from_hostent(gethostbyname(name), perr);
110 }
111
112 static char *get_canon_name_from_addr(struct in_addr ip,
113                                 int *perr)
114 {
115         return canon_name_from_hostent(
116                         gethostbyaddr((void *)&ip, sizeof ip, AF_INET),
117                         perr);
118 }
119
120 static struct addrinfo *alloc_entry(const struct addrinfo *hints,
121                                 struct in_addr ip,
122                                 unsigned short port)
123 {
124         struct sockaddr_in *psin = NULL;
125         struct addrinfo *ai = SMB_MALLOC(sizeof(*ai));
126
127         if (!ai) {
128                 return NULL;
129         }
130         memset(ai, '\0', sizeof(*ai));
131
132         psin = SMB_MALLOC(sizeof(*psin));
133         if (!psin) {
134                 free(ai);
135                 return NULL;
136         }
137
138         memset(psin, '\0', sizeof(*psin));
139
140         psin->sin_family = AF_INET;
141         psin->sin_port = htons(port);
142         psin->sin_addr = ip;
143
144         ai->ai_flags = 0;
145         ai->ai_family = AF_INET;
146         ai->ai_socktype = hints->ai_socktype;
147         ai->ai_protocol = hints->ai_protocol;
148         ai->ai_addrlen = sizeof(*psin);
149         ai->ai_addr = (struct sockaddr *) psin;
150         ai->ai_canonname = NULL;
151         ai->ai_next = NULL;
152
153         return ai;
154 }
155
156 /*
157  * get address info for a single ipv4 address.
158  *
159  *      Bugs:   - servname can only be a number, not text.
160  */
161
162 static int getaddr_info_single_addr(const char *service,
163                                 uint32 addr,
164                                 const struct addrinfo *hints,
165                                 struct addrinfo **res)
166 {
167
168         struct addrinfo *ai = NULL;
169         struct in_addr ip;
170         unsigned short port = 0;
171
172         if (service) {
173                 port = (unsigned short)atoi(service);
174         }
175         ip.s_addr = htonl(addr);
176
177         ai = alloc_entry(hints, ip, port);
178         if (!ai) {
179                 return EAI_MEMORY;
180         }
181
182         /* If we're asked for the canonical name,
183          * make sure it returns correctly. */
184         if (!(hints->ai_flags & AI_NUMERICSERV) &&
185                         hints->ai_flags & AI_CANONNAME) {
186                 int err;
187                 if (addr == INADDR_LOOPBACK || addr == INADDR_ANY) {
188                         ai->ai_canonname = get_my_canon_name(&err);
189                 } else {
190                         ai->ai_canonname =
191                         get_canon_name_from_addr(ip,&err);
192                 }
193                 if (ai->ai_canonname == NULL) {
194                         freeaddrinfo(ai);
195                         return err;
196                 }
197         }
198
199         *res = ai;
200         return 0;
201 }
202
203 /*
204  * get address info for multiple ipv4 addresses.
205  *
206  *      Bugs:   - servname can only be a number, not text.
207  */
208
209 static int getaddr_info_name(const char *node,
210                                 const char *service,
211                                 const struct addrinfo *hints,
212                                 struct addrinfo **res)
213 {
214         struct addrinfo *listp = NULL, *prevp = NULL;
215         char **pptr = NULL;
216         int err;
217         struct hostent *hp = NULL;
218         unsigned short port = 0;
219
220         if (service) {
221                 port = (unsigned short)atoi(service);
222         }
223
224         hp = gethostbyname(node);
225         err = check_hostent_err(hp);
226         if (err) {
227                 return err;
228         }
229
230         for(pptr = hp->h_addr_list; *pptr; pptr++) {
231                 struct in_addr ip = *(struct in_addr *)*pptr;
232                 struct addrinfo *ai = alloc_entry(hints, ip, port);
233
234                 if (!ai) {
235                         freeaddrinfo(listp);
236                         return EAI_MEMORY;
237                 }
238
239                 if (!listp) {
240                         listp = ai;
241                         prevp = ai;
242                         ai->ai_canonname = SMB_STRDUP(hp->h_name);
243                         if (!ai->ai_canonname) {
244                                 freeaddrinfo(listp);
245                                 return EAI_MEMORY;
246                         }
247                 } else {
248                         prevp->ai_next = ai;
249                         prevp = ai;
250                 }
251         }
252         *res = listp;
253         return 0;
254 }
255
256 /*
257  * get address info for ipv4 sockets.
258  *
259  *      Bugs:   - servname can only be a number, not text.
260  */
261
262 int getaddrinfo(const char *node,
263                 const char *service,
264                 const struct addrinfo * hintp,
265                 struct addrinfo ** res)
266 {
267         struct addrinfo hints;
268
269         /* Setup the hints struct. */
270         if (hintp == NULL) {
271                 memset(&hints, 0, sizeof(hints));
272                 hints.ai_family = AF_INET;
273                 hints.ai_socktype = SOCK_STREAM;
274         } else {
275                 memcpy(&hints, hintp, sizeof(hints));
276         }
277
278         if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) {
279                 return EAI_FAMILY;
280         }
281
282         if (hints.ai_socktype == 0) {
283                 hints.ai_socktype = SOCK_STREAM;
284         }
285
286         if (!node && !service) {
287                 return EAI_NONAME;
288         }
289
290         if (node) {
291                 if (node[0] == '\0') {
292                         return getaddr_info_single_addr(service,
293                                         INADDR_ANY,
294                                         &hints,
295                                         res);
296                 } else if (hints.ai_flags & AI_NUMERICHOST) {
297                         struct in_addr ip;
298                         if (!inet_aton(node, &ip)) {
299                                 return EAI_FAIL;
300                         }
301                         return getaddr_info_single_addr(service,
302                                         ntohl(ip.s_addr),
303                                         &hints,
304                                         res);
305                 } else {
306                         return getaddr_info_name(node,
307                                                 service,
308                                                 &hints,
309                                                 res);
310                 }
311         } else if (hints.ai_flags & AI_PASSIVE) {
312                 return getaddr_info_single_addr(service,
313                                         INADDR_ANY,
314                                         &hints,
315                                         res);
316         }
317         return getaddr_info_single_addr(service,
318                                         INADDR_LOOPBACK,
319                                         &hints,
320                                         res);
321 }
322
323
324 void freeaddrinfo(struct addrinfo *res)
325 {
326         struct addrinfo *next = NULL;
327
328         for (;res; res = next) {
329                 next = res->ai_next;
330                 if (res->ai_canonname) {
331                         free(res->ai_canonname);
332                 }
333                 if (res->ai_addr) {
334                         free(res->ai_addr);
335                 }
336                 free(res);
337         }
338 }
339
340
341 const char *gai_strerror(int errcode)
342 {
343 #ifdef HAVE_HSTRERROR
344         int                     hcode;
345
346         switch (errcode)
347         {
348                 case EAI_NONAME:
349                         hcode = HOST_NOT_FOUND;
350                         break;
351                 case EAI_AGAIN:
352                         hcode = TRY_AGAIN;
353                         break;
354                 case EAI_FAIL:
355                 default:
356                         hcode = NO_RECOVERY;
357                         break;
358         }
359
360         return hstrerror(hcode);
361 #else                                                   /* !HAVE_HSTRERROR */
362
363         switch (errcode)
364         {
365                 case EAI_NONAME:
366                         return "Unknown host";
367                 case EAI_AGAIN:
368                         return "Host name lookup failure";
369 #ifdef EAI_BADFLAGS
370                 case EAI_BADFLAGS:
371                         return "Invalid argument";
372 #endif
373 #ifdef EAI_FAMILY
374                 case EAI_FAMILY:
375                         return "Address family not supported";
376 #endif
377 #ifdef EAI_MEMORY
378                 case EAI_MEMORY:
379                         return "Not enough memory";
380 #endif
381 #ifdef EAI_NODATA
382                 case EAI_NODATA:
383                         return "No host data of that type was found";
384 #endif
385 #ifdef EAI_SERVICE
386                 case EAI_SERVICE:
387                         return "Class type not found";
388 #endif
389 #ifdef EAI_SOCKTYPE
390                 case EAI_SOCKTYPE:
391                         return "Socket type not supported";
392 #endif
393                 default:
394                         return "Unknown server error";
395         }
396 #endif   /* HAVE_HSTRERROR */
397 }
398
399 static int gethostnameinfo(const struct sockaddr *sa,
400                         char *node,
401                         size_t nodelen,
402                         int flags)
403 {
404         int ret = -1;
405         char *p = NULL;
406
407         if (!(flags & NI_NUMERICHOST)) {
408                 struct hostent *hp = gethostbyaddr(
409                                 (void *)&((struct sockaddr_in *)sa)->sin_addr,
410                                 sizeof (struct in_addr),
411                                 sa->sa_family);
412                 ret = check_hostent_err(hp);
413                 if (ret == 0) {
414                         /* Name looked up successfully. */
415                         ret = snprintf(node, nodelen, "%s", hp->h_name);
416                         if (ret < 0 || (size_t)ret >= nodelen) {
417                                 return EAI_MEMORY;
418                         }
419                         if (flags & NI_NOFQDN) {
420                                 p = strchr(node,'.');
421                                 if (p) {
422                                         *p = '\0';
423                                 }
424                         }
425                         return 0;
426                 }
427
428                 if (flags & NI_NAMEREQD) {
429                         /* If we require a name and didn't get one,
430                          * automatically fail. */
431                         return ret;
432                 }
433                 /* Otherwise just fall into the numeric host code... */
434         }
435         p = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr);
436         ret = snprintf(node, nodelen, "%s", p);
437         if (ret < 0 || (size_t)ret >= nodelen) {
438                 return EAI_MEMORY;
439         }
440         return 0;
441 }
442
443 static int getservicenameinfo(const struct sockaddr *sa,
444                         char *service,
445                         size_t servicelen,
446                         int flags)
447 {
448         int ret = -1;
449         int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
450
451         if (!(flags & NI_NUMERICSERV)) {
452                 struct servent *se = getservbyport(
453                                 port,
454                                 (flags & NI_DGRAM) ? "udp" : "tcp");
455                 if (se && se->s_name) {
456                         /* Service name looked up successfully. */
457                         ret = snprintf(service, servicelen, "%s", se->s_name);
458                         if (ret < 0 || (size_t)ret >= servicelen) {
459                                 return EAI_MEMORY;
460                         }
461                         return 0;
462                 }
463                 /* Otherwise just fall into the numeric service code... */
464         }
465         ret = snprintf(service, servicelen, "%d", port);
466         if (ret < 0 || (size_t)ret >= servicelen) {
467                 return EAI_MEMORY;
468         }
469         return 0;
470 }
471
472 /*
473  * Convert an ipv4 address to a hostname.
474  *
475  * Bugs:        - No IPv6 support.
476  */
477 int getnameinfo(const struct sockaddr *sa, socklen_t salen,
478                         char *node, size_t nodelen,
479                         char *service, size_t servicelen, int flags)
480 {
481
482         /* Invalid arguments. */
483         if (sa == NULL || (node == NULL && service == NULL)) {
484                 return EAI_FAIL;
485         }
486
487         if (sa->sa_family != AF_INET) {
488                 return EAI_FAIL;
489         }
490
491         if (salen < (socklen_t)sizeof (struct sockaddr_in)) {
492                 return EAI_FAIL;
493         }
494
495         if (node) {
496                 int ret = gethostnameinfo(sa, node, nodelen, flags);
497                 if (ret)
498                         return ret;
499         }
500
501         if (service) {
502                 return getservicenameinfo(sa, service, servicelen, flags);
503         }
504         return 0;
505 }