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*50957Skarels static char sccsid[] = "@(#)domain.c 5.27 (Berkeley) 09/03/91 (with name server)"; 1435653Seric #else 15*50957Skarels static char sccsid[] = "@(#)domain.c 5.27 (Berkeley) 09/03/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) 85*50957Skarels 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 16229653Sbloom getcanonname(host, hbsize) 16329653Sbloom char *host; 16429653Sbloom int hbsize; 16529653Sbloom { 16640277Sbostic extern int h_errno; 16733929Sbostic register u_char *eom, *cp; 16833929Sbostic register int n; 16929653Sbloom HEADER *hp; 17033929Sbostic querybuf answer; 17129653Sbloom u_short type; 17233929Sbostic int first, ancount, qdcount, loopcnt; 17333929Sbostic char nbuf[PACKETSZ]; 17429653Sbloom 17533929Sbostic loopcnt = 0; 17633929Sbostic loop: 17736483Sbostic /* 17836483Sbostic * Use query type of ANY if possible (NO_WILDCARD_MX), which will 17936483Sbostic * find types CNAME, A, and MX, and will cause all existing records 18036483Sbostic * to be cached by our local server. If there is (might be) a 18136483Sbostic * wildcard MX record in the local domain or its parents that are 18236483Sbostic * searched, we can't use ANY; it would cause fully-qualified names 18336483Sbostic * to match as names in a local domain. 18436483Sbostic */ 18550537Seric n = res_search(host, C_IN, WildcardMX ? T_CNAME : T_ANY, 18650537Seric (char *)&answer, sizeof(answer)); 18729653Sbloom if (n < 0) { 18833929Sbostic if (tTd(8, 1)) 18933929Sbostic printf("getcanonname: res_search failed (errno=%d, h_errno=%d)\n", 19033929Sbostic errno, h_errno); 19129653Sbloom return; 19229653Sbloom } 19333929Sbostic 19433929Sbostic /* find first satisfactory answer */ 19533929Sbostic hp = (HEADER *)&answer; 19629653Sbloom ancount = ntohs(hp->ancount); 19733929Sbostic 19833929Sbostic /* we don't care about errors here, only if we got an answer */ 19929653Sbloom if (ancount == 0) { 20033929Sbostic if (tTd(8, 1)) 20129653Sbloom printf("rcode = %d, ancount=%d\n", hp->rcode, ancount); 20229653Sbloom return; 20329653Sbloom } 20433929Sbostic cp = (u_char *)&answer + sizeof(HEADER); 20533929Sbostic eom = (u_char *)&answer + n; 20633929Sbostic for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 207*50957Skarels if ((n = dn_skipname(cp, eom)) < 0) 20833929Sbostic return; 20933929Sbostic 21033929Sbostic /* 21133929Sbostic * just in case someone puts a CNAME record after another record, 21233929Sbostic * check all records for CNAME; otherwise, just take the first 21333929Sbostic * name found. 21433929Sbostic */ 21533929Sbostic for (first = 1; --ancount >= 0 && cp < eom; cp += n) { 21646928Sbostic if ((n = dn_expand((u_char *)&answer, 21746928Sbostic eom, cp, (u_char *)nbuf, sizeof(nbuf))) < 0) 21829653Sbloom break; 21933929Sbostic if (first) { /* XXX */ 22029653Sbloom (void)strncpy(host, nbuf, hbsize); 22129653Sbloom host[hbsize - 1] = '\0'; 22229653Sbloom first = 0; 22329653Sbloom } 22429653Sbloom cp += n; 22533929Sbostic GETSHORT(type, cp); 22629653Sbloom cp += sizeof(u_short) + sizeof(u_long); 22733929Sbostic GETSHORT(n, cp); 22829653Sbloom if (type == T_CNAME) { 22929653Sbloom /* 23033929Sbostic * assume that only one cname will be found. More 23133929Sbostic * than one is undefined. Copy so that if dn_expand 23233929Sbostic * fails, `host' is still okay. 23329653Sbloom */ 23446928Sbostic if ((n = dn_expand((u_char *)&answer, 23546928Sbostic eom, cp, (u_char *)nbuf, sizeof(nbuf))) < 0) 23629653Sbloom break; 23733929Sbostic (void)strncpy(host, nbuf, hbsize); /* XXX */ 23829653Sbloom host[hbsize - 1] = '\0'; 23933929Sbostic if (++loopcnt > 8) /* never be more than 1 */ 24033929Sbostic return; 24133929Sbostic goto loop; 24229653Sbloom } 24329653Sbloom } 24429653Sbloom } 24535653Seric 24636494Sphil #else /* not NAMED_BIND */ 24736494Sphil 24836494Sphil #include <netdb.h> 24936494Sphil 25036494Sphil getcanonname(host, hbsize) 25136494Sphil char *host; 25236494Sphil int hbsize; 25336494Sphil { 25436494Sphil struct hostent *hp; 25536494Sphil 25636494Sphil hp = gethostbyname(host); 25736494Sphil if (hp == NULL) 25836494Sphil return; 25936494Sphil 26036494Sphil if (strlen(hp->h_name) >= hbsize) 26136494Sphil return; 26236494Sphil 26336494Sphil (void) strcpy(host, hp->h_name); 26436494Sphil } 26536494Sphil 26636494Sphil #endif /* not NAMED_BIND */ 267