Use "use warnings" rather than -w on the #! line.
[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;
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
324void 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
341const 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
399static 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(
94564346
WD
409 (void *)&((struct sockaddr_in *)sa)->sin_addr,
410 sizeof (struct in_addr),
9f802c72
WD
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
443static 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 */
477int 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
94564346 491 if (salen < (socklen_t)sizeof (struct sockaddr_in)) {
9f802c72
WD
492 return EAI_FAIL;
493 }
494
9f802c72 495 if (node) {
9ddc2b64
WD
496 int ret = gethostnameinfo(sa, node, nodelen, flags);
497 if (ret)
498 return ret;
9f802c72
WD
499 }
500
501 if (service) {
502 return getservicenameinfo(sa, service, servicelen, flags);
503 }
504 return 0;
505}