Annotation of embedaddon/rsync/lib/getaddrinfo.c, revision 1.1
1.1 ! misho 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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>