|
|
1.1 root 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: #ifndef lint
21: static char sccsid[] = "@(#)getinfo.c 5.22 (Berkeley) 6/1/90";
22: #endif /* not lint */
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: unsigned 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, (char *)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: NULL, 0, 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.