Commit | Line | Data |
---|---|---|
9f802c72 WD |
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 | } |