1: /* 2: * Copyright (c) 1985,1989 Regents of the University of California. 3: * All rights reserved. 4: * 5: * Redistribution and use in source and binary forms are permitted provided 6: * that: (1) source distributions retain this entire copyright notice and 7: * comment, and (2) distributions including binaries display the following 8: * acknowledgement: ``This product includes software developed by the 9: * University of California, Berkeley and its contributors'' in the 10: * documentation or other materials provided with the distribution and in 11: * all advertising materials mentioning features or use of this software. 12: * Neither the name of the University nor the names of its contributors may 13: * be used to endorse or promote products derived from this software without 14: * specific prior written permission. 15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 16: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 17: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 18: */ 19: 20: #if !defined(lint) && defined(DOSCCS) 21: static char sccsid[] = "@(#)getinfo.c 5.22 (Berkeley) 6/1/90"; 22: #endif 23: 24: /* 25: ******************************************************************************* 26: * 27: * getinfo.c -- 28: * 29: * Routines to create requests to name servers 30: * and interpret the answers. 31: * 32: * Adapted from 4.3BSD BIND gethostnamadr.c 33: * 34: ******************************************************************************* 35: */ 36: 37: #include <sys/types.h> 38: #include <sys/socket.h> 39: #include <netinet/in.h> 40: #include <stdio.h> 41: #include <ctype.h> 42: #include <arpa/nameser.h> 43: #include <arpa/inet.h> 44: #include <resolv.h> 45: #include "res.h" 46: 47: extern char *_res_resultcodes[]; 48: extern char *res_skip(); 49: 50: #define MAXALIASES 35 51: #define MAXADDRS 35 52: #define MAXDOMAINS 35 53: #define MAXSERVERS 10 54: 55: static char *addr_list[MAXADDRS + 1]; 56: 57: static char *host_aliases[MAXALIASES]; 58: static int host_aliases_len[MAXALIASES]; 59: static char hostbuf[BUFSIZ+1]; 60: 61: typedef struct { 62: char *name; 63: char *domain[MAXDOMAINS]; 64: int numDomains; 65: char *address[MAXADDRS]; 66: int numAddresses; 67: } ServerTable; 68: 69: ServerTable server[MAXSERVERS]; 70: 71: typedef union { 72: HEADER qb1; 73: char qb2[PACKETSZ]; 74: } querybuf; 75: 76: typedef union { 77: long al; 78: char ac; 79: } align; 80: 81: #define GetShort(cp) _getshort(cp); cp += sizeof(unsigned short); 82: 83: 84: /* 85: ******************************************************************************* 86: * 87: * GetAnswer -- 88: * 89: * Interprets an answer packet and retrieves the following 90: * information: 91: * 92: * Results: 93: * SUCCESS the info was retrieved. 94: * NO_INFO the packet did not contain an answer. 95: * NONAUTH non-authoritative information was found. 96: * ERROR the answer was malformed. 97: * Other errors returned in the packet header. 98: * 99: ******************************************************************************* 100: */ 101: 102: static int 103: GetAnswer(nsAddrPtr, queryType, msg, msglen, iquery, hostPtr, isServer) 104: struct in_addr *nsAddrPtr; 105: char *msg; 106: int queryType; 107: int msglen; 108: Boolean iquery; 109: register HostInfo *hostPtr; 110: Boolean isServer; 111: { 112: register HEADER *headerPtr; 113: register char *cp; 114: querybuf answer; 115: char *eom, *bp, **aliasPtr; 116: char **addrPtr; 117: char *namePtr; 118: char *dnamePtr; 119: int type, class; 120: int qdcount, ancount, arcount, nscount, buflen; 121: int origClass; 122: int numAliases = 0; 123: int numAddresses = 0; 124: int n, i, j; 125: int len; 126: int dlen; 127: int status; 128: int numServers; 129: Boolean haveAnswer; 130: Boolean printedAnswers = FALSE; 131: 132: 133: /* 134: * If the hostPtr was used before, free up the calloc'd areas. 135: */ 136: FreeHostInfoPtr(hostPtr); 137: 138: status = SendRequest(nsAddrPtr, msg, msglen, (char *) &answer, 139: sizeof(answer), &n); 140: 141: if (status != SUCCESS) { 142: if (_res.options & RES_DEBUG2) 143: printf("SendRequest failed\n"); 144: return (status); 145: } 146: eom = (char *) &answer + n; 147: 148: headerPtr = (HEADER *) &answer; 149: 150: if (headerPtr->rcode != NOERROR) { 151: return (headerPtr->rcode); 152: } 153: 154: qdcount = ntohs(headerPtr->qdcount); 155: ancount = ntohs(headerPtr->ancount); 156: arcount = ntohs(headerPtr->arcount); 157: nscount = ntohs(headerPtr->nscount); 158: 159: /* 160: * If there are no answer, n.s. or additional records 161: * then return with an error. 162: */ 163: if (ancount == 0 && nscount == 0 && arcount == 0) { 164: return (NO_INFO); 165: } 166: 167: 168: bp = hostbuf; 169: buflen = sizeof(hostbuf); 170: cp = (char *) &answer + sizeof(HEADER); 171: 172: /* Skip over question section. */ 173: while (qdcount-- > 0) { 174: cp += dn_skipname(cp, eom) + QFIXEDSZ; 175: } 176: 177: aliasPtr = host_aliases; 178: addrPtr = addr_list; 179: haveAnswer = FALSE; 180: 181: /* 182: * Scan through the answer resource records. 183: * Answers for address query types are saved. 184: * Other query type answers are just printed. 185: */ 186: if (ancount != 0) { 187: if (!isServer && !headerPtr->aa) { 188: printf("Non-authoritative answer:\n"); 189: } 190: 191: if (queryType != T_A && !(iquery && queryType == T_PTR)) { 192: while (--ancount >= 0 && cp < eom) { 193: if ((cp = Print_rr(cp, (char *)&answer, eom, stdout)) == NULL) { 194: return(ERROR); 195: } 196: } 197: printedAnswers = TRUE; 198: } else { 199: while (--ancount >= 0 && cp < eom) { 200: if ((n = dn_expand((char *)&answer, eom, cp, bp, buflen)) < 0) { 201: return(ERROR); 202: } 203: cp += n; 204: type = GetShort(cp); 205: class = GetShort(cp); 206: cp += sizeof(u_long); /* skip TTL */ 207: dlen = GetShort(cp); 208: if (type == T_CNAME) { 209: /* 210: * Found an alias. 211: */ 212: cp += dlen; 213: if (aliasPtr >= &host_aliases[MAXALIASES-1]) { 214: continue; 215: } 216: *aliasPtr++ = bp; 217: n = strlen(bp) + 1; 218: host_aliases_len[numAliases] = n; 219: numAliases++; 220: bp += n; 221: buflen -= n; 222: continue; 223: } else if (type == T_PTR) { 224: /* 225: * Found a "pointer" to the real name. 226: */ 227: if((n=dn_expand((char *)&answer, eom, cp, bp,buflen)) < 0) { 228: cp += n; 229: continue; 230: } 231: cp += n; 232: len = strlen(bp) + 1; 233: hostPtr->name = Calloc(1, len); 234: bcopy(bp, hostPtr->name, len); 235: haveAnswer = TRUE; 236: break; 237: } else if (type != T_A) { 238: cp += dlen; 239: continue; 240: } 241: if (haveAnswer) { 242: /* 243: * If we've already got 1 address, we aren't interested 244: * in addresses with a different length or class. 245: */ 246: if (dlen != hostPtr->addrLen) { 247: cp += dlen; 248: continue; 249: } 250: if (class != origClass) { 251: cp += dlen; 252: continue; 253: } 254: } else { 255: /* 256: * First address: record its length and class so we 257: * only save additonal ones with the same attributes. 258: */ 259: hostPtr->addrLen = dlen; 260: origClass = class; 261: hostPtr->addrType = (class == C_IN) ? AF_INET : AF_UNSPEC; 262: len = strlen(bp) + 1; 263: hostPtr->name = Calloc(1, len); 264: bcopy(bp, hostPtr->name, len); 265: } 266: bp += (((u_long)bp) % sizeof(align)); 267: 268: if (bp + dlen >= &hostbuf[sizeof(hostbuf)]) { 269: if (_res.options & RES_DEBUG) { 270: printf("Size (%d) too big\n", dlen); 271: } 272: break; 273: } 274: bcopy(cp, *addrPtr++ = bp, dlen); 275: bp +=dlen; 276: cp += dlen; 277: numAddresses++; 278: haveAnswer = TRUE; 279: } 280: } 281: } 282: 283: if ((queryType == T_A || queryType == T_PTR) && haveAnswer) { 284: 285: /* 286: * Go through the alias and address lists and return them 287: * in the hostPtr variable. 288: */ 289: 290: if (numAliases > 0) { 291: hostPtr->aliases = (char **) Calloc(1 + numAliases, sizeof(char *)); 292: for (i = 0; i < numAliases; i++) { 293: hostPtr->aliases[i] = Calloc(1, host_aliases_len[i]); 294: bcopy(host_aliases[i], hostPtr->aliases[i],host_aliases_len[i]); 295: } 296: hostPtr->aliases[i] = NULL; 297: } 298: if (numAddresses > 0) { 299: hostPtr->addrList = (char **)Calloc(1+numAddresses, sizeof(char *)); 300: for (i = 0; i < numAddresses; i++) { 301: hostPtr->addrList[i] = Calloc(1, hostPtr->addrLen); 302: bcopy(addr_list[i], hostPtr->addrList[i], hostPtr->addrLen); 303: } 304: hostPtr->addrList[i] = NULL; 305: } 306: #ifdef verbose 307: if (headerPtr->aa || nscount == 0) { 308: hostPtr->servers = NULL; 309: return (SUCCESS); 310: } 311: #else 312: hostPtr->servers = NULL; 313: return (SUCCESS); 314: #endif 315: } 316: 317: /* 318: * At this point, for the T_A query type, only empty answers remain. 319: * For other query types, additional information might be found 320: * in the additional resource records part. 321: */ 322: 323: if (!headerPtr->aa && (queryType != T_A) && (nscount > 0 || arcount > 0)) { 324: if (printedAnswers) { 325: putchar('\n'); 326: } 327: printf("Authoritative answers can be found from:\n"); 328: } 329: 330: cp = res_skip((char *) &answer, 2, eom); 331: 332: numServers = 0; 333: if (queryType != T_A) { 334: /* 335: * If we don't need to save the record, just print it. 336: */ 337: while (--nscount >= 0 && cp < eom) { 338: if ((cp = Print_rr(cp, (char *) &answer, eom, stdout)) == NULL) { 339: return(ERROR); 340: } 341: } 342: } else { 343: while (--nscount >= 0 && cp < eom) { 344: /* 345: * Go through the NS records and retrieve the names of hosts 346: * that serve the requested domain. 347: */ 348: 349: if ((n = dn_expand((char *) &answer, eom, cp, bp, buflen)) < 0) { 350: return(ERROR); 351: } 352: cp += n; 353: len = strlen(bp) + 1; 354: dnamePtr = Calloc(1, len); /* domain name */ 355: bcopy(bp, dnamePtr, len); 356: 357: type = GetShort(cp); 358: class = GetShort(cp); 359: cp += sizeof(u_long); /* skip TTL */ 360: dlen = GetShort(cp); 361: 362: if (type != T_NS) { 363: cp += dlen; 364: } else { 365: Boolean found; 366: 367: if ((n = dn_expand((char *) &answer, eom, cp, bp, buflen)) < 0){ 368: return(ERROR); 369: } 370: cp += n; 371: len = strlen(bp) + 1; 372: namePtr = Calloc(1, len); /* server host name */ 373: bcopy(bp, namePtr, len); 374: 375: /* 376: * Store the information keyed by the server host name. 377: */ 378: found = FALSE; 379: for (j = 0; j < numServers; j++) { 380: if (strcmp(namePtr, server[j].name) == 0) { 381: found = TRUE; 382: free(namePtr); 383: break; 384: } 385: } 386: if (found) { 387: server[j].numDomains++; 388: if (server[j].numDomains <= MAXDOMAINS) { 389: server[j].domain[server[j].numDomains-1] = dnamePtr; 390: } 391: } else { 392: if (numServers >= MAXSERVERS) { 393: break; 394: } 395: server[numServers].name = namePtr; 396: server[numServers].domain[0] = dnamePtr; 397: server[numServers].numDomains = 1; 398: server[numServers].numAddresses = 0; 399: numServers++; 400: } 401: } 402: } 403: } 404: 405: /* 406: * Additional resource records contain addresses of servers. 407: */ 408: cp = res_skip((char *) &answer, 3, eom); 409: 410: if (queryType != T_A) { 411: /* 412: * If we don't need to save the record, just print it. 413: */ 414: while (--arcount >= 0 && cp < eom) { 415: if ((cp = Print_rr(cp, (char *) &answer, eom, stdout)) == NULL) { 416: return(ERROR); 417: } 418: } 419: } else { 420: while (--arcount >= 0 && cp < eom) { 421: if ((n = dn_expand((char *) &answer, eom, cp, bp, buflen)) < 0) { 422: break; 423: } 424: cp += n; 425: type = GetShort(cp); 426: class = GetShort(cp); 427: cp += sizeof(u_long); /* skip TTL */ 428: dlen = GetShort(cp); 429: 430: if (type != T_A) { 431: cp += dlen; 432: continue; 433: } else { 434: for (j = 0; j < numServers; j++) { 435: if (strcmp(bp, server[j].name) == 0) { 436: server[j].numAddresses++; 437: if (server[j].numAddresses <= MAXADDRS) { 438: server[j].address[server[j].numAddresses-1] = 439: Calloc(1,dlen); 440: bcopy(cp, 441: server[j].address[server[j].numAddresses-1],dlen); 442: break; 443: } 444: } 445: } 446: cp += dlen; 447: } 448: } 449: } 450: 451: /* 452: * If we are returning name server info, transfer it to the hostPtr. 453: */ 454: if (numServers > 0) { 455: hostPtr->servers = (ServerInfo **) 456: Calloc(numServers+1, sizeof(ServerInfo *)); 457: 458: for (i = 0; i < numServers; i++) { 459: hostPtr->servers[i] = (ServerInfo *) Calloc(1, sizeof(ServerInfo)); 460: hostPtr->servers[i]->name = server[i].name; 461: 462: 463: hostPtr->servers[i]->domains = (char **) 464: Calloc(server[i].numDomains+1,sizeof(char *)); 465: for (j = 0; j < server[i].numDomains; j++) { 466: hostPtr->servers[i]->domains[j] = server[i].domain[j]; 467: } 468: hostPtr->servers[i]->domains[j] = NULL; 469: 470: 471: hostPtr->servers[i]->addrList = (char **) 472: Calloc(server[i].numAddresses+1,sizeof(char *)); 473: for (j = 0; j < server[i].numAddresses; j++) { 474: hostPtr->servers[i]->addrList[j] = server[i].address[j]; 475: } 476: hostPtr->servers[i]->addrList[j] = NULL; 477: 478: } 479: hostPtr->servers[i] = NULL; 480: } 481: 482: switch (queryType) { 483: case T_A: 484: return NONAUTH; 485: case T_PTR: 486: if (iquery) 487: return NO_INFO; 488: /* fall through */ 489: default: 490: return SUCCESS; 491: } 492: } 493: 494: /* 495: ******************************************************************************* 496: * 497: * GetHostInfo -- 498: * 499: * Retrieves host name, address and alias information 500: * for a domain. 501: * 502: * Algorithm from res_search(). 503: * 504: * Results: 505: * ERROR - res_mkquery failed. 506: * + return values from GetAnswer() 507: * 508: ******************************************************************************* 509: */ 510: 511: int 512: GetHostInfoByName(nsAddrPtr, queryClass, queryType, name, hostPtr, isServer) 513: struct in_addr *nsAddrPtr; 514: int queryClass; 515: int queryType; 516: char *name; 517: HostInfo *hostPtr; 518: Boolean isServer; 519: { 520: int n; 521: register int result; 522: register char *cp, **domain; 523: extern char *hostalias(); 524: Boolean got_nodata = FALSE; 525: u_long ina; 526: 527: /* Catch explicit addresses */ 528: if ((queryType == T_A) && IsAddr(name, &ina)) { 529: hostPtr->name = Calloc(strlen(name)+3, 1); 530: (void)sprintf(hostPtr->name,"[%s]",name); 531: hostPtr->aliases = NULL; 532: hostPtr->servers = NULL; 533: hostPtr->addrType = AF_INET; 534: hostPtr->addrLen = sizeof(struct in_addr); 535: hostPtr->addrList = (char **)Calloc(2, sizeof(char *)); 536: hostPtr->addrList[0] = Calloc(sizeof(long), sizeof(char)); 537: bcopy((char *)&ina, hostPtr->addrList[0], sizeof(ina)); 538: hostPtr->addrList[1] = NULL; 539: return(SUCCESS); 540: } 541: 542: result = NXDOMAIN; 543: for (cp = name, n = 0; *cp; cp++) 544: if (*cp == '.') 545: n++; 546: if (n == 0 && (cp = hostalias(name))) { 547: printf("Aliased to \"%s\"\n\n", cp); 548: return (GetHostDomain(nsAddrPtr, queryClass, queryType, 549: cp, (char *)NULL, hostPtr, isServer)); 550: } 551: /* 552: * We do at least one level of search if 553: * - there is no dot and RES_DEFNAME is set, or 554: * - there is at least one dot, there is no trailing dot, 555: * and RES_DNSRCH is set. 556: */ 557: if ((n == 0 && _res.options & RES_DEFNAMES) || 558: (n != 0 && *--cp != '.' && _res.options & RES_DNSRCH)) 559: for (domain = _res.dnsrch; *domain; domain++) { 560: result = GetHostDomain(nsAddrPtr, queryClass, queryType, 561: name, *domain, hostPtr, isServer); 562: /* 563: * If no server present, give up. 564: * If name isn't found in this domain, 565: * keep trying higher domains in the search list 566: * (if that's enabled). 567: * On a NO_INFO error, keep trying, otherwise 568: * a wildcard entry of another type could keep us 569: * from finding this entry higher in the domain. 570: * If we get some other error (negative answer or 571: * server failure), then stop searching up, 572: * but try the input name below in case it's fully-qualified. 573: */ 574: if (result == SUCCESS || result == NO_RESPONSE) 575: return result; 576: if (result == NO_INFO) 577: got_nodata++; 578: if ((result != NXDOMAIN && result != NO_INFO) || 579: (_res.options & RES_DNSRCH) == 0) 580: break; 581: } 582: /* 583: * If the search/default failed, try the name as fully-qualified, 584: * but only if it contained at least one dot (even trailing). 585: * This is purely a heuristic; we assume that any reasonable query 586: * about a top-level domain (for servers, SOA, etc) will not use 587: * res_search. 588: */ 589: if (n && (result = GetHostDomain(nsAddrPtr, queryClass, queryType, 590: name, (char *)NULL, hostPtr, isServer)) == SUCCESS) 591: return result; 592: if (got_nodata) 593: result = NO_INFO; 594: return (result); 595: } 596: 597: /* 598: * Perform a query on the concatenation of name and domain, 599: * removing a trailing dot from name if domain is NULL. 600: */ 601: GetHostDomain(nsAddrPtr, queryClass, queryType, name, domain, hostPtr, isServer) 602: struct in_addr *nsAddrPtr; 603: int queryClass; 604: int queryType; 605: char *name, *domain; 606: HostInfo *hostPtr; 607: Boolean isServer; 608: { 609: querybuf buf; 610: char nbuf[2*MAXDNAME+2]; 611: char *longname = nbuf; 612: int n; 613: 614: if (domain == NULL) { 615: /* 616: * Check for trailing '.'; 617: * copy without '.' if present. 618: */ 619: n = strlen(name) - 1; 620: if (name[n] == '.' && n < sizeof(nbuf) - 1) { 621: bcopy(name, nbuf, n); 622: nbuf[n] = '\0'; 623: } else 624: longname = name; 625: } else { 626: (void)sprintf(nbuf, "%.*s.%.*s", 627: MAXDNAME, name, MAXDNAME, domain); 628: longname = nbuf; 629: } 630: n = res_mkquery(QUERY, longname, queryClass, queryType, 631: (char *)0, 0, (struct rrec *)0, (char *) &buf, sizeof(buf)); 632: if (n < 0) { 633: if (_res.options & RES_DEBUG) { 634: printf("Res_mkquery failed\n"); 635: } 636: return (ERROR); 637: } 638: 639: n = GetAnswer(nsAddrPtr, queryType, (char *)&buf, n, 0, hostPtr, isServer); 640: 641: /* 642: * GetAnswer didn't find a name, so set it to the specified one. 643: */ 644: if (n == NONAUTH) { 645: if (hostPtr->name == NULL) { 646: int len = strlen(longname) + 1; 647: hostPtr->name = Calloc(len, sizeof(char)); 648: bcopy(longname, hostPtr->name, len); 649: } 650: } 651: return(n); 652: } 653: 654: 655: /* 656: ******************************************************************************* 657: * 658: * GetHostInfoByAddr -- 659: * 660: * Performs an inverse query to find the host name 661: * that corresponds to the given address. 662: * 663: * Results: 664: * ERROR - res_mkquery failed. 665: * + return values from GetAnswer() 666: * 667: ******************************************************************************* 668: */ 669: 670: int 671: GetHostInfoByAddr(nsAddrPtr, address, hostPtr) 672: struct in_addr *nsAddrPtr; 673: struct in_addr *address; 674: HostInfo *hostPtr; 675: { 676: int n; 677: querybuf buf; 678: char qbuf[MAXDNAME]; 679: char *p = (char *) &address->s_addr; 680: 681: (void)sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa", 682: ((unsigned)p[3] & 0xff), 683: ((unsigned)p[2] & 0xff), 684: ((unsigned)p[1] & 0xff), 685: ((unsigned)p[0] & 0xff)); 686: n = res_mkquery(QUERY, qbuf, C_IN, T_PTR, 687: (char *)NULL, 0, (struct rrec *)NULL, (char *) &buf, sizeof(buf)); 688: if (n < 0) { 689: if (_res.options & RES_DEBUG) { 690: printf("res_mkquery() failed\n"); 691: } 692: return (ERROR); 693: } 694: n = GetAnswer(nsAddrPtr, T_PTR, (char *) &buf, n, 1, hostPtr, 1); 695: if (n == SUCCESS) { 696: hostPtr->addrType = AF_INET; 697: hostPtr->addrLen = 4; 698: hostPtr->addrList = (char **)Calloc(2, sizeof(char *)); 699: hostPtr->addrList[0] = Calloc(sizeof(long), sizeof(char)); 700: bcopy((char *)p, hostPtr->addrList[0], sizeof(struct in_addr)); 701: hostPtr->addrList[1] = NULL; 702: } 703: return n; 704: } 705: 706: /* 707: ******************************************************************************* 708: * 709: * FreeHostInfoPtr -- 710: * 711: * Deallocates all the calloc'd areas for a HostInfo variable. 712: * 713: ******************************************************************************* 714: */ 715: 716: void 717: FreeHostInfoPtr(hostPtr) 718: register HostInfo *hostPtr; 719: { 720: int i, j; 721: 722: if (hostPtr->name != NULL) { 723: free(hostPtr->name); 724: hostPtr->name = NULL; 725: } 726: 727: if (hostPtr->aliases != NULL) { 728: i = 0; 729: while (hostPtr->aliases[i] != NULL) { 730: free(hostPtr->aliases[i]); 731: i++; 732: } 733: free((char *)hostPtr->aliases); 734: hostPtr->aliases = NULL; 735: } 736: 737: if (hostPtr->addrList != NULL) { 738: i = 0; 739: while (hostPtr->addrList[i] != NULL) { 740: free(hostPtr->addrList[i]); 741: i++; 742: } 743: free((char *)hostPtr->addrList); 744: hostPtr->addrList = NULL; 745: } 746: 747: if (hostPtr->servers != NULL) { 748: i = 0; 749: while (hostPtr->servers[i] != NULL) { 750: 751: if (hostPtr->servers[i]->name != NULL) { 752: free(hostPtr->servers[i]->name); 753: } 754: 755: if (hostPtr->servers[i]->domains != NULL) { 756: j = 0; 757: while (hostPtr->servers[i]->domains[j] != NULL) { 758: free(hostPtr->servers[i]->domains[j]); 759: j++; 760: } 761: free((char *)hostPtr->servers[i]->domains); 762: } 763: 764: if (hostPtr->servers[i]->addrList != NULL) { 765: j = 0; 766: while (hostPtr->servers[i]->addrList[j] != NULL) { 767: free(hostPtr->servers[i]->addrList[j]); 768: j++; 769: } 770: free((char *)hostPtr->servers[i]->addrList); 771: } 772: free((char *)hostPtr->servers[i]); 773: i++; 774: } 775: free((char *)hostPtr->servers); 776: hostPtr->servers = NULL; 777: } 778: }