129432Sbloom /* 234921Sbostic * Copyright (c) 1986 Eric P. Allman 333778Sbostic * Copyright (c) 1988 Regents of the University of California. 433778Sbostic * All rights reserved. 533778Sbostic * 642826Sbostic * %sccs.include.redist.c% 733778Sbostic */ 829432Sbloom 940961Sbostic #include "sendmail.h" 1035653Seric 1129432Sbloom #ifndef lint 1235653Seric #ifdef NAMED_BIND 13*51910Seric static char sccsid[] = "@(#)domain.c 5.32 (Berkeley) 12/13/91 (with name server)"; 1435653Seric #else 15*51910Seric static char sccsid[] = "@(#)domain.c 5.32 (Berkeley) 12/13/91 (without name server)"; 1635653Seric #endif 1733778Sbostic #endif /* not lint */ 1829432Sbloom 1935653Seric #ifdef NAMED_BIND 2035653Seric 2133929Sbostic #include <sys/param.h> 2235653Seric #include <errno.h> 2333929Sbostic #include <arpa/nameser.h> 2433929Sbostic #include <resolv.h> 2533929Sbostic #include <netdb.h> 2629432Sbloom 2729432Sbloom typedef union { 2829432Sbloom HEADER qb1; 2929432Sbloom char qb2[PACKETSZ]; 3029432Sbloom } querybuf; 3129432Sbloom 3233929Sbostic static char hostbuf[MAXMXHOSTS*PACKETSZ]; 3329432Sbloom 3433929Sbostic getmxrr(host, mxhosts, localhost, rcode) 3533929Sbostic char *host, **mxhosts, *localhost; 3633929Sbostic int *rcode; 3729432Sbloom { 3833929Sbostic extern int h_errno; 3933929Sbostic register u_char *eom, *cp; 4033929Sbostic register int i, j, n, nmx; 4133929Sbostic register char *bp; 4229432Sbloom HEADER *hp; 4333929Sbostic querybuf answer; 4433929Sbostic int ancount, qdcount, buflen, seenlocal; 4533929Sbostic u_short pref, localpref, type, prefer[MAXMXHOSTS]; 4629432Sbloom 4736483Sbostic errno = 0; 4833929Sbostic n = res_search(host, C_IN, T_MX, (char *)&answer, sizeof(answer)); 4935653Seric if (n < 0) 5035653Seric { 5133929Sbostic if (tTd(8, 1)) 5233929Sbostic printf("getmxrr: res_search failed (errno=%d, h_errno=%d)\n", 5333929Sbostic errno, h_errno); 5435653Seric switch (h_errno) 5535653Seric { 5635653Seric case NO_DATA: 5735653Seric case NO_RECOVERY: 5835653Seric /* no MX data on this host */ 5933929Sbostic goto punt; 6035653Seric 6135653Seric case HOST_NOT_FOUND: 6235653Seric /* the host just doesn't exist */ 6333929Sbostic *rcode = EX_NOHOST; 6433929Sbostic break; 6535653Seric 6635653Seric case TRY_AGAIN: 6735653Seric /* couldn't connect to the name server */ 6835653Seric if (!UseNameServer && errno == ECONNREFUSED) 6935653Seric goto punt; 7035653Seric 7135653Seric /* it might come up later; better queue it up */ 7233929Sbostic *rcode = EX_TEMPFAIL; 7333929Sbostic break; 7429432Sbloom } 7535653Seric 7635653Seric /* irreconcilable differences */ 7735653Seric return (-1); 7829432Sbloom } 7933929Sbostic 8033929Sbostic /* find first satisfactory answer */ 8133929Sbostic hp = (HEADER *)&answer; 8233929Sbostic cp = (u_char *)&answer + sizeof(HEADER); 8333929Sbostic eom = (u_char *)&answer + n; 8433929Sbostic for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 8550957Skarels if ((n = dn_skipname(cp, eom)) < 0) 8633929Sbostic goto punt; 8729432Sbloom nmx = 0; 8829551Sbloom seenlocal = 0; 8929432Sbloom buflen = sizeof(hostbuf); 9033929Sbostic bp = hostbuf; 9133929Sbostic ancount = ntohs(hp->ancount); 9233929Sbostic while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS) { 9346928Sbostic if ((n = dn_expand((u_char *)&answer, 9446928Sbostic eom, cp, (u_char *)bp, buflen)) < 0) 9529432Sbloom break; 9629432Sbloom cp += n; 9733929Sbostic GETSHORT(type, cp); 9829432Sbloom cp += sizeof(u_short) + sizeof(u_long); 9933929Sbostic GETSHORT(n, cp); 10029432Sbloom if (type != T_MX) { 10129432Sbloom if (tTd(8, 1) || _res.options & RES_DEBUG) 10229432Sbloom printf("unexpected answer type %d, size %d\n", 10333929Sbostic type, n); 10429432Sbloom cp += n; 10529432Sbloom continue; 10629432Sbloom } 10733929Sbostic GETSHORT(pref, cp); 10846928Sbostic if ((n = dn_expand((u_char *)&answer, 10946928Sbostic eom, cp, (u_char *)bp, buflen)) < 0) 11029432Sbloom break; 11129551Sbloom cp += n; 11233929Sbostic if (!strcasecmp(bp, localhost)) { 11333929Sbostic if (seenlocal == 0 || pref < localpref) 11433929Sbostic localpref = pref; 11529551Sbloom seenlocal = 1; 11629551Sbloom continue; 11729551Sbloom } 11829432Sbloom prefer[nmx] = pref; 11929432Sbloom mxhosts[nmx++] = bp; 12033929Sbostic n = strlen(bp) + 1; 12133929Sbostic bp += n; 12233929Sbostic buflen -= n; 12329432Sbloom } 12429551Sbloom if (nmx == 0) { 12533929Sbostic punt: mxhosts[0] = strcpy(hostbuf, host); 12629551Sbloom return(1); 12729551Sbloom } 12833929Sbostic 12929432Sbloom /* sort the records */ 13029432Sbloom for (i = 0; i < nmx; i++) { 13129432Sbloom for (j = i + 1; j < nmx; j++) { 13236483Sbostic if (prefer[i] > prefer[j] || 13336483Sbostic (prefer[i] == prefer[j] && rand() % 1 == 0)) { 13433929Sbostic register int temp; 13533929Sbostic register char *temp1; 13629432Sbloom 13729432Sbloom temp = prefer[i]; 13829432Sbloom prefer[i] = prefer[j]; 13929432Sbloom prefer[j] = temp; 14029432Sbloom temp1 = mxhosts[i]; 14129432Sbloom mxhosts[i] = mxhosts[j]; 14229432Sbloom mxhosts[j] = temp1; 14329432Sbloom } 14429432Sbloom } 14533929Sbostic if (seenlocal && prefer[i] >= localpref) { 14629551Sbloom /* 14733929Sbostic * truncate higher pref part of list; if we're 14833929Sbostic * the best choice left, we should have realized 14933929Sbostic * awhile ago that this was a local delivery. 15029551Sbloom */ 15133929Sbostic if (i == 0) { 15233929Sbostic *rcode = EX_CONFIG; 15333929Sbostic return(-1); 15429551Sbloom } 15533929Sbostic nmx = i; 15629551Sbloom break; 15729551Sbloom } 15829432Sbloom } 15929432Sbloom return(nmx); 16029432Sbloom } 16129653Sbloom 16251324Seric /* 16351324Seric * Use query type of ANY if possible (NoWildcardMX), which will 16451324Seric * find types CNAME, A, and MX, and will cause all existing records 16551324Seric * to be cached by our local server. If there is (might be) a 16651324Seric * wildcard MX record in the local domain or its parents that are 16751324Seric * searched, we can't use ANY; it would cause fully-qualified names 16851324Seric * to match as names in a local domain. 16951324Seric */ 17051324Seric 17151314Seric bool 17229653Sbloom getcanonname(host, hbsize) 17329653Sbloom char *host; 17429653Sbloom int hbsize; 17529653Sbloom { 17640277Sbostic extern int h_errno; 17751324Seric register u_char *eom, *ap; 17851324Seric register char *cp; 17933929Sbostic register int n; 18029653Sbloom HEADER *hp; 18133929Sbostic querybuf answer; 18233929Sbostic int first, ancount, qdcount, loopcnt; 18351324Seric int ret; 18451324Seric int qtype = NoWildcardMX ? T_ANY : T_CNAME; 18551324Seric char **domain; 18651324Seric bool rval; 18751324Seric int type; 18833929Sbostic char nbuf[PACKETSZ]; 18929653Sbloom 19051324Seric if (tTd(8, 2)) 19151324Seric printf("getcanonname(%s)\n", host); 19251324Seric 19351324Seric if ((_res.options & RES_INIT) == 0 && res_init() == -1) 19451324Seric return (FALSE); 19551324Seric 19633929Sbostic loopcnt = 0; 19751324Seric rval = FALSE; 19833929Sbostic loop: 19951324Seric for (cp = host, n = 0; *cp; cp++) 20051324Seric if (*cp == '.') 20151324Seric n++; 20251324Seric if (n > 0 && *--cp == '.') 20351324Seric { 20451324Seric cp = host; 20551324Seric n = -1; 20651324Seric } 20751324Seric 20836483Sbostic /* 20951324Seric * We do at least one level of search if 21051324Seric * - there is no dot and RES_DEFNAME is set, or 21151324Seric * - there is at least one dot, there is no trailing dot, 21251324Seric * and RES_DNSRCH is set. 21336483Sbostic */ 21451324Seric ret = -1; 21551324Seric if ((n == 0 && _res.options & RES_DEFNAMES) || 21651324Seric (n > 0 && *--cp != '.' && _res.options & RES_DNSRCH)) 21751324Seric { 21851324Seric for (domain = _res.dnsrch; *domain; domain++) 21951324Seric { 22051324Seric (void) sprintf(nbuf, "%.*s.%.*s", 22151324Seric MAXDNAME, host, MAXDNAME, *domain); 22251324Seric if (tTd(8, 5)) 22351324Seric printf("getcanonname: trying %s\n", nbuf); 22451324Seric ret = res_query(nbuf, C_IN, qtype, &answer, sizeof(answer)); 22551324Seric if (ret > 0) 22651324Seric { 22751324Seric if (tTd(8, 8)) 22851324Seric printf("\tYES\n"); 22951324Seric cp = nbuf; 23051324Seric break; 23151324Seric } 23251324Seric else if (tTd(8, 8)) 23351324Seric printf("\tNO: h_errno=%d\n", h_errno); 23451324Seric 23551324Seric /* 23651324Seric * If no server present, give up. 23751324Seric * If name isn't found in this domain, 23851324Seric * keep trying higher domains in the search list 23951324Seric * (if that's enabled). 24051324Seric * On a NO_DATA error, keep trying, otherwise 24151324Seric * a wildcard entry of another type could keep us 24251324Seric * from finding this entry higher in the domain. 24351324Seric * If we get some other error (negative answer or 24451324Seric * server failure), then stop searching up, 24551324Seric * but try the input name below in case it's fully-qualified. 24651324Seric */ 24751324Seric if (errno == ECONNREFUSED) { 24851324Seric h_errno = TRY_AGAIN; 249*51910Seric return FALSE; 25051324Seric } 25151324Seric if (h_errno == NO_DATA) 25251324Seric { 25351324Seric ret = 0; 25451324Seric cp = nbuf; 25551324Seric break; 25651324Seric } 25751324Seric if ((h_errno != HOST_NOT_FOUND) || 25851324Seric (_res.options & RES_DNSRCH) == 0) 259*51910Seric return FALSE; 26051324Seric } 26129653Sbloom } 26251324Seric if (ret < 0) 26351324Seric { 264*51910Seric /* 265*51910Seric ** Try the unmodified name. 266*51910Seric */ 267*51910Seric 26851324Seric cp = host; 26951324Seric if (tTd(8, 5)) 27051324Seric printf("getcanonname: trying %s\n", cp); 27151324Seric ret = res_query(cp, C_IN, qtype, &answer, sizeof(answer)); 27251324Seric if (ret > 0) 27351324Seric { 27451324Seric if (tTd(8, 8)) 27551324Seric printf("\tYES\n"); 27651324Seric } 27751324Seric else 27851324Seric { 27951324Seric if (tTd(8, 8)) 28051324Seric printf("\tNO: h_errno=%d\n", h_errno); 281*51910Seric if (h_errno != NO_DATA) 282*51910Seric return FALSE; 28351324Seric } 28451324Seric } 28533929Sbostic 28633929Sbostic /* find first satisfactory answer */ 28733929Sbostic hp = (HEADER *)&answer; 28829653Sbloom ancount = ntohs(hp->ancount); 28951324Seric if (tTd(8, 3)) 29051324Seric printf("rcode = %d, ancount=%d, qdcount=%d\n", 29151324Seric hp->rcode, ancount, ntohs(hp->qdcount)); 29233929Sbostic 29333929Sbostic /* we don't care about errors here, only if we got an answer */ 29451324Seric if (ancount == 0) 29551324Seric { 29651324Seric strncpy(host, cp, hbsize); 29751324Seric host[hbsize - 1] = '\0'; 29851324Seric return (TRUE); 29929653Sbloom } 30051324Seric ap = (u_char *)&answer + sizeof(HEADER); 30151324Seric eom = (u_char *)&answer + ret; 30251324Seric for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) 30351324Seric { 30451324Seric if ((ret = dn_skipname(ap, eom)) < 0) 30551324Seric { 30651324Seric if (tTd(8, 20)) 30751324Seric printf("qdcount failure (%d)\n", 30851324Seric ntohs(hp->qdcount)); 309*51910Seric return FALSE; /* ???XXX??? */ 31051324Seric } 31151324Seric } 31233929Sbostic 31333929Sbostic /* 31451324Seric * just in case someone puts a CNAME record after another record, 31551324Seric * check all records for CNAME; otherwise, just take the first 31651324Seric * name found. 31751324Seric */ 31851324Seric for (first = 1; --ancount >= 0 && ap < eom; ap += ret) 31951324Seric { 32051324Seric if ((ret = dn_expand((u_char *)&answer, 32151324Seric eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) 32229653Sbloom break; 32333929Sbostic if (first) { /* XXX */ 32429653Sbloom (void)strncpy(host, nbuf, hbsize); 32529653Sbloom host[hbsize - 1] = '\0'; 32629653Sbloom first = 0; 32751324Seric rval = TRUE; 32829653Sbloom } 32951324Seric ap += ret; 33051324Seric GETSHORT(type, ap); 33151324Seric ap += sizeof(u_short) + sizeof(u_long); 33251324Seric GETSHORT(ret, ap); 33329653Sbloom if (type == T_CNAME) { 33429653Sbloom /* 33533929Sbostic * assume that only one cname will be found. More 33633929Sbostic * than one is undefined. Copy so that if dn_expand 33733929Sbostic * fails, `host' is still okay. 33829653Sbloom */ 33951324Seric if ((ret = dn_expand((u_char *)&answer, 34051324Seric eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) 34129653Sbloom break; 34233929Sbostic (void)strncpy(host, nbuf, hbsize); /* XXX */ 34329653Sbloom host[hbsize - 1] = '\0'; 34433929Sbostic if (++loopcnt > 8) /* never be more than 1 */ 34551324Seric return FALSE; 34651324Seric rval = TRUE; 34733929Sbostic goto loop; 34829653Sbloom } 34929653Sbloom } 35051324Seric return rval; /* ???XXX??? */ 35129653Sbloom } 35235653Seric 35336494Sphil #else /* not NAMED_BIND */ 35436494Sphil 35536494Sphil #include <netdb.h> 35636494Sphil 35751314Seric bool 35836494Sphil getcanonname(host, hbsize) 35936494Sphil char *host; 36036494Sphil int hbsize; 36136494Sphil { 36236494Sphil struct hostent *hp; 36336494Sphil 36436494Sphil hp = gethostbyname(host); 36536494Sphil if (hp == NULL) 36651314Seric return (FALSE); 36736494Sphil 36836494Sphil if (strlen(hp->h_name) >= hbsize) 36951314Seric return (FALSE); 37036494Sphil 37136494Sphil (void) strcpy(host, hp->h_name); 37251314Seric return (TRUE); 37336494Sphil } 37436494Sphil 37536494Sphil #endif /* not NAMED_BIND */ 376