Fix the val reading for MSG_ERROR_EXIT. Use 0-length MSG_DATA when
[rsync/rsync.git] / lib / getaddrinfo.c
CommitLineData
9f802c72
WD
1/*
2PostgreSQL Database Management System
3(formerly known as Postgres, then as Postgres95)
4
5Portions Copyright (c) 1996-2005, The PostgreSQL Global Development Group
6
7Portions Copyright (c) 1994, The Regents of the University of California
8
9Permission to use, copy, modify, and distribute this software and its
10documentation for any purpose, without fee, and without a written agreement
11is hereby granted, provided that the above copyright notice and this paragraph
12and the following two paragraphs appear in all copies.
13
14IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
15DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
16LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
17EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
18SUCH DAMAGE.
19
20THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
21INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
23ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
24TO 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
94564346
WD
56#ifndef HOST_NAME_MAX
57#define HOST_NAME_MAX 255
58#endif
59
9f802c72
WD
60static int check_hostent_err(struct hostent *hp)
61{
94564346
WD
62#ifndef INET6
63 extern int h_errno;
64#endif
9f802c72
WD
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
83static 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
99static 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
112static char *get_canon_name_from_addr(struct in_addr ip,
113 int *perr)
114{
115 return canon_name_from_hostent(
1b411143 116 gethostbyaddr((void *)&ip, sizeof ip, AF_INET),
9f802c72
WD
117 perr);
118}
119
120static 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
162static int getaddr_info_single_addr(const char *service,
268da816 163 uint32 addr,
9f802c72
WD
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
209static 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
262int 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;
87755c6c 298 if (inet_pton(AF_INET, node, &ip) <= 0)
9f802c72 299 return EAI_FAIL;
9f802c72
WD
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
323void 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
340const 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
398static 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(
94564346
WD
408 (void *)&((struct sockaddr_in *)sa)->sin_addr,
409 sizeof (struct in_addr),
9f802c72
WD
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
442static 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 */
476int 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
94564346 490 if (salen < (socklen_t)sizeof (struct sockaddr_in)) {
9f802c72
WD
491 return EAI_FAIL;
492 }
493
9f802c72 494 if (node) {
9ddc2b64
WD
495 int ret = gethostnameinfo(sa, node, nodelen, flags);
496 if (ret)
497 return ret;
9f802c72
WD
498 }
499
500 if (service) {
501 return getservicenameinfo(sa, service, servicelen, flags);
502 }
503 return 0;
504}