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