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