Add back a define of "struct sockaddr_storage" for systems that
[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
56static 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
76static 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
92static 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
105static 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
113static 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
155static 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
202static 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
255int 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
317void 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
334const 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
392static 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
436static 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 */
470int 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}