129432Sbloom /* 234921Sbostic * Copyright (c) 1986 Eric P. Allman 333778Sbostic * Copyright (c) 1988 Regents of the University of California. 433778Sbostic * All rights reserved. 533778Sbostic * 633778Sbostic * Redistribution and use in source and binary forms are permitted 734921Sbostic * provided that the above copyright notice and this paragraph are 834921Sbostic * duplicated in all such forms and that any documentation, 934921Sbostic * advertising materials, and other materials related to such 1034921Sbostic * distribution and use acknowledge that the software was developed 1134921Sbostic * by the University of California, Berkeley. The name of the 1234921Sbostic * University may not be used to endorse or promote products derived 1334921Sbostic * from this software without specific prior written permission. 1434921Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1534921Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1634921Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1733778Sbostic */ 1829432Sbloom 1935653Seric #include <sendmail.h> 2035653Seric 2129432Sbloom #ifndef lint 2235653Seric #ifdef NAMED_BIND 23*40277Sbostic static char sccsid[] = "@(#)domain.c 5.20 (Berkeley) 03/05/90 (with name server)"; 2435653Seric #else 25*40277Sbostic static char sccsid[] = "@(#)domain.c 5.20 (Berkeley) 03/05/90 (without name server)"; 2635653Seric #endif 2733778Sbostic #endif /* not lint */ 2829432Sbloom 2935653Seric #ifdef NAMED_BIND 3035653Seric 3133929Sbostic #include <sys/param.h> 3235653Seric #include <errno.h> 3333929Sbostic #include <arpa/nameser.h> 3433929Sbostic #include <resolv.h> 3533929Sbostic #include <netdb.h> 3629432Sbloom 3729432Sbloom typedef union { 3829432Sbloom HEADER qb1; 3929432Sbloom char qb2[PACKETSZ]; 4029432Sbloom } querybuf; 4129432Sbloom 4233929Sbostic static char hostbuf[MAXMXHOSTS*PACKETSZ]; 4329432Sbloom 4433929Sbostic getmxrr(host, mxhosts, localhost, rcode) 4533929Sbostic char *host, **mxhosts, *localhost; 4633929Sbostic int *rcode; 4729432Sbloom { 4833929Sbostic extern int h_errno; 4933929Sbostic register u_char *eom, *cp; 5033929Sbostic register int i, j, n, nmx; 5133929Sbostic register char *bp; 5229432Sbloom HEADER *hp; 5333929Sbostic querybuf answer; 5433929Sbostic int ancount, qdcount, buflen, seenlocal; 5533929Sbostic u_short pref, localpref, type, prefer[MAXMXHOSTS]; 5629432Sbloom 5736483Sbostic errno = 0; 5833929Sbostic n = res_search(host, C_IN, T_MX, (char *)&answer, sizeof(answer)); 5935653Seric if (n < 0) 6035653Seric { 6133929Sbostic if (tTd(8, 1)) 6233929Sbostic printf("getmxrr: res_search failed (errno=%d, h_errno=%d)\n", 6333929Sbostic errno, h_errno); 6435653Seric switch (h_errno) 6535653Seric { 6635653Seric case NO_DATA: 6735653Seric case NO_RECOVERY: 6835653Seric /* no MX data on this host */ 6933929Sbostic goto punt; 7035653Seric 7135653Seric case HOST_NOT_FOUND: 7235653Seric /* the host just doesn't exist */ 7333929Sbostic *rcode = EX_NOHOST; 7433929Sbostic break; 7535653Seric 7635653Seric case TRY_AGAIN: 7735653Seric /* couldn't connect to the name server */ 7835653Seric if (!UseNameServer && errno == ECONNREFUSED) 7935653Seric goto punt; 8035653Seric 8135653Seric /* it might come up later; better queue it up */ 8233929Sbostic *rcode = EX_TEMPFAIL; 8333929Sbostic break; 8429432Sbloom } 8535653Seric 8635653Seric /* irreconcilable differences */ 8735653Seric return (-1); 8829432Sbloom } 8933929Sbostic 9033929Sbostic /* find first satisfactory answer */ 9133929Sbostic hp = (HEADER *)&answer; 9233929Sbostic cp = (u_char *)&answer + sizeof(HEADER); 9333929Sbostic eom = (u_char *)&answer + n; 9433929Sbostic for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 9533929Sbostic if ((n = dn_skipname(cp, eom)) < 0) 9633929Sbostic goto punt; 9729432Sbloom nmx = 0; 9829551Sbloom seenlocal = 0; 9929432Sbloom buflen = sizeof(hostbuf); 10033929Sbostic bp = hostbuf; 10133929Sbostic ancount = ntohs(hp->ancount); 10233929Sbostic while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS) { 10329432Sbloom if ((n = dn_expand((char *)&answer, eom, cp, bp, buflen)) < 0) 10429432Sbloom break; 10529432Sbloom cp += n; 10633929Sbostic GETSHORT(type, cp); 10729432Sbloom cp += sizeof(u_short) + sizeof(u_long); 10833929Sbostic GETSHORT(n, cp); 10929432Sbloom if (type != T_MX) { 11029432Sbloom if (tTd(8, 1) || _res.options & RES_DEBUG) 11129432Sbloom printf("unexpected answer type %d, size %d\n", 11233929Sbostic type, n); 11329432Sbloom cp += n; 11429432Sbloom continue; 11529432Sbloom } 11633929Sbostic GETSHORT(pref, cp); 11729432Sbloom if ((n = dn_expand((char *)&answer, eom, cp, bp, buflen)) < 0) 11829432Sbloom break; 11929551Sbloom cp += n; 12033929Sbostic if (!strcasecmp(bp, localhost)) { 12133929Sbostic if (seenlocal == 0 || pref < localpref) 12233929Sbostic localpref = pref; 12329551Sbloom seenlocal = 1; 12429551Sbloom continue; 12529551Sbloom } 12629432Sbloom prefer[nmx] = pref; 12729432Sbloom mxhosts[nmx++] = bp; 12833929Sbostic n = strlen(bp) + 1; 12933929Sbostic bp += n; 13033929Sbostic buflen -= n; 13129432Sbloom } 13229551Sbloom if (nmx == 0) { 13333929Sbostic punt: mxhosts[0] = strcpy(hostbuf, host); 13429551Sbloom return(1); 13529551Sbloom } 13633929Sbostic 13729432Sbloom /* sort the records */ 13829432Sbloom for (i = 0; i < nmx; i++) { 13929432Sbloom for (j = i + 1; j < nmx; j++) { 14036483Sbostic if (prefer[i] > prefer[j] || 14136483Sbostic (prefer[i] == prefer[j] && rand() % 1 == 0)) { 14233929Sbostic register int temp; 14333929Sbostic register char *temp1; 14429432Sbloom 14529432Sbloom temp = prefer[i]; 14629432Sbloom prefer[i] = prefer[j]; 14729432Sbloom prefer[j] = temp; 14829432Sbloom temp1 = mxhosts[i]; 14929432Sbloom mxhosts[i] = mxhosts[j]; 15029432Sbloom mxhosts[j] = temp1; 15129432Sbloom } 15229432Sbloom } 15333929Sbostic if (seenlocal && prefer[i] >= localpref) { 15429551Sbloom /* 15533929Sbostic * truncate higher pref part of list; if we're 15633929Sbostic * the best choice left, we should have realized 15733929Sbostic * awhile ago that this was a local delivery. 15829551Sbloom */ 15933929Sbostic if (i == 0) { 16033929Sbostic *rcode = EX_CONFIG; 16133929Sbostic return(-1); 16229551Sbloom } 16333929Sbostic nmx = i; 16429551Sbloom break; 16529551Sbloom } 16629432Sbloom } 16729432Sbloom return(nmx); 16829432Sbloom } 16929653Sbloom 17029653Sbloom getcanonname(host, hbsize) 17129653Sbloom char *host; 17229653Sbloom int hbsize; 17329653Sbloom { 174*40277Sbostic extern int h_errno; 17533929Sbostic register u_char *eom, *cp; 17633929Sbostic register int n; 17729653Sbloom HEADER *hp; 17833929Sbostic querybuf answer; 17929653Sbloom u_short type; 18033929Sbostic int first, ancount, qdcount, loopcnt; 18133929Sbostic char nbuf[PACKETSZ]; 18229653Sbloom 18333929Sbostic loopcnt = 0; 18433929Sbostic loop: 18536483Sbostic /* 18636483Sbostic * Use query type of ANY if possible (NO_WILDCARD_MX), which will 18736483Sbostic * find types CNAME, A, and MX, and will cause all existing records 18836483Sbostic * to be cached by our local server. If there is (might be) a 18936483Sbostic * wildcard MX record in the local domain or its parents that are 19036483Sbostic * searched, we can't use ANY; it would cause fully-qualified names 19136483Sbostic * to match as names in a local domain. 19236483Sbostic */ 19336483Sbostic # ifdef NO_WILDCARD_MX 19436483Sbostic n = res_search(host, C_IN, T_ANY, (char *)&answer, sizeof(answer)); 19536483Sbostic # else 19634158Sbostic n = res_search(host, C_IN, T_CNAME, (char *)&answer, sizeof(answer)); 19736483Sbostic # endif 19829653Sbloom if (n < 0) { 19933929Sbostic if (tTd(8, 1)) 20033929Sbostic printf("getcanonname: res_search failed (errno=%d, h_errno=%d)\n", 20133929Sbostic errno, h_errno); 20229653Sbloom return; 20329653Sbloom } 20433929Sbostic 20533929Sbostic /* find first satisfactory answer */ 20633929Sbostic hp = (HEADER *)&answer; 20729653Sbloom ancount = ntohs(hp->ancount); 20833929Sbostic 20933929Sbostic /* we don't care about errors here, only if we got an answer */ 21029653Sbloom if (ancount == 0) { 21133929Sbostic if (tTd(8, 1)) 21229653Sbloom printf("rcode = %d, ancount=%d\n", hp->rcode, ancount); 21329653Sbloom return; 21429653Sbloom } 21533929Sbostic cp = (u_char *)&answer + sizeof(HEADER); 21633929Sbostic eom = (u_char *)&answer + n; 21733929Sbostic for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 21833929Sbostic if ((n = dn_skipname(cp, eom)) < 0) 21933929Sbostic return; 22033929Sbostic 22133929Sbostic /* 22233929Sbostic * just in case someone puts a CNAME record after another record, 22333929Sbostic * check all records for CNAME; otherwise, just take the first 22433929Sbostic * name found. 22533929Sbostic */ 22633929Sbostic for (first = 1; --ancount >= 0 && cp < eom; cp += n) { 22729653Sbloom if ((n = dn_expand((char *)&answer, eom, cp, nbuf, 22829653Sbloom sizeof(nbuf))) < 0) 22929653Sbloom break; 23033929Sbostic if (first) { /* XXX */ 23129653Sbloom (void)strncpy(host, nbuf, hbsize); 23229653Sbloom host[hbsize - 1] = '\0'; 23329653Sbloom first = 0; 23429653Sbloom } 23529653Sbloom cp += n; 23633929Sbostic GETSHORT(type, cp); 23729653Sbloom cp += sizeof(u_short) + sizeof(u_long); 23833929Sbostic GETSHORT(n, cp); 23929653Sbloom if (type == T_CNAME) { 24029653Sbloom /* 24133929Sbostic * assume that only one cname will be found. More 24233929Sbostic * than one is undefined. Copy so that if dn_expand 24333929Sbostic * fails, `host' is still okay. 24429653Sbloom */ 24529653Sbloom if ((n = dn_expand((char *)&answer, eom, cp, nbuf, 24629653Sbloom sizeof(nbuf))) < 0) 24729653Sbloom break; 24833929Sbostic (void)strncpy(host, nbuf, hbsize); /* XXX */ 24929653Sbloom host[hbsize - 1] = '\0'; 25033929Sbostic if (++loopcnt > 8) /* never be more than 1 */ 25133929Sbostic return; 25233929Sbostic goto loop; 25329653Sbloom } 25429653Sbloom } 25529653Sbloom } 25635653Seric 25736494Sphil #else /* not NAMED_BIND */ 25836494Sphil 25936494Sphil #include <netdb.h> 26036494Sphil 26136494Sphil getcanonname(host, hbsize) 26236494Sphil char *host; 26336494Sphil int hbsize; 26436494Sphil { 26536494Sphil struct hostent *hp; 26636494Sphil 26736494Sphil hp = gethostbyname(host); 26836494Sphil if (hp == NULL) 26936494Sphil return; 27036494Sphil 27136494Sphil if (strlen(hp->h_name) >= hbsize) 27236494Sphil return; 27336494Sphil 27436494Sphil (void) strcpy(host, hp->h_name); 27536494Sphil } 27636494Sphil 27736494Sphil #endif /* not NAMED_BIND */ 278