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*36494Sphil static char sccsid[] = "@(#)domain.c 5.18 (Berkeley) 01/01/89 (with name server)"; 2435653Seric #else 25*36494Sphil static char sccsid[] = "@(#)domain.c 5.18 (Berkeley) 01/01/89 (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 { 6129432Sbloom #ifdef DEBUG 6233929Sbostic if (tTd(8, 1)) 6333929Sbostic printf("getmxrr: res_search failed (errno=%d, h_errno=%d)\n", 6433929Sbostic errno, h_errno); 6529432Sbloom #endif 6635653Seric switch (h_errno) 6735653Seric { 6835653Seric case NO_DATA: 6935653Seric case NO_RECOVERY: 7035653Seric /* no MX data on this host */ 7133929Sbostic goto punt; 7235653Seric 7335653Seric case HOST_NOT_FOUND: 7435653Seric /* the host just doesn't exist */ 7533929Sbostic *rcode = EX_NOHOST; 7633929Sbostic break; 7735653Seric 7835653Seric case TRY_AGAIN: 7935653Seric /* couldn't connect to the name server */ 8035653Seric if (!UseNameServer && errno == ECONNREFUSED) 8135653Seric goto punt; 8235653Seric 8335653Seric /* it might come up later; better queue it up */ 8433929Sbostic *rcode = EX_TEMPFAIL; 8533929Sbostic break; 8629432Sbloom } 8735653Seric 8835653Seric /* irreconcilable differences */ 8935653Seric return (-1); 9029432Sbloom } 9133929Sbostic 9233929Sbostic /* find first satisfactory answer */ 9333929Sbostic hp = (HEADER *)&answer; 9433929Sbostic cp = (u_char *)&answer + sizeof(HEADER); 9533929Sbostic eom = (u_char *)&answer + n; 9633929Sbostic for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 9733929Sbostic if ((n = dn_skipname(cp, eom)) < 0) 9833929Sbostic goto punt; 9929432Sbloom nmx = 0; 10029551Sbloom seenlocal = 0; 10129432Sbloom buflen = sizeof(hostbuf); 10233929Sbostic bp = hostbuf; 10333929Sbostic ancount = ntohs(hp->ancount); 10433929Sbostic while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS) { 10529432Sbloom if ((n = dn_expand((char *)&answer, eom, cp, bp, buflen)) < 0) 10629432Sbloom break; 10729432Sbloom cp += n; 10833929Sbostic GETSHORT(type, cp); 10929432Sbloom cp += sizeof(u_short) + sizeof(u_long); 11033929Sbostic GETSHORT(n, cp); 11129432Sbloom if (type != T_MX) { 11229432Sbloom #ifdef DEBUG 11329432Sbloom if (tTd(8, 1) || _res.options & RES_DEBUG) 11429432Sbloom printf("unexpected answer type %d, size %d\n", 11533929Sbostic type, n); 11629432Sbloom #endif 11729432Sbloom cp += n; 11829432Sbloom continue; 11929432Sbloom } 12033929Sbostic GETSHORT(pref, cp); 12129432Sbloom if ((n = dn_expand((char *)&answer, eom, cp, bp, buflen)) < 0) 12229432Sbloom break; 12329551Sbloom cp += n; 12433929Sbostic if (!strcasecmp(bp, localhost)) { 12533929Sbostic if (seenlocal == 0 || pref < localpref) 12633929Sbostic localpref = pref; 12729551Sbloom seenlocal = 1; 12829551Sbloom continue; 12929551Sbloom } 13029432Sbloom prefer[nmx] = pref; 13129432Sbloom mxhosts[nmx++] = bp; 13233929Sbostic n = strlen(bp) + 1; 13333929Sbostic bp += n; 13433929Sbostic buflen -= n; 13529432Sbloom } 13629551Sbloom if (nmx == 0) { 13733929Sbostic punt: mxhosts[0] = strcpy(hostbuf, host); 13829551Sbloom return(1); 13929551Sbloom } 14033929Sbostic 14129432Sbloom /* sort the records */ 14229432Sbloom for (i = 0; i < nmx; i++) { 14329432Sbloom for (j = i + 1; j < nmx; j++) { 14436483Sbostic if (prefer[i] > prefer[j] || 14536483Sbostic (prefer[i] == prefer[j] && rand() % 1 == 0)) { 14633929Sbostic register int temp; 14733929Sbostic register char *temp1; 14829432Sbloom 14929432Sbloom temp = prefer[i]; 15029432Sbloom prefer[i] = prefer[j]; 15129432Sbloom prefer[j] = temp; 15229432Sbloom temp1 = mxhosts[i]; 15329432Sbloom mxhosts[i] = mxhosts[j]; 15429432Sbloom mxhosts[j] = temp1; 15529432Sbloom } 15629432Sbloom } 15733929Sbostic if (seenlocal && prefer[i] >= localpref) { 15829551Sbloom /* 15933929Sbostic * truncate higher pref part of list; if we're 16033929Sbostic * the best choice left, we should have realized 16133929Sbostic * awhile ago that this was a local delivery. 16229551Sbloom */ 16333929Sbostic if (i == 0) { 16433929Sbostic *rcode = EX_CONFIG; 16533929Sbostic return(-1); 16629551Sbloom } 16733929Sbostic nmx = i; 16829551Sbloom break; 16929551Sbloom } 17029432Sbloom } 17129432Sbloom return(nmx); 17229432Sbloom } 17329653Sbloom 17429653Sbloom getcanonname(host, hbsize) 17529653Sbloom char *host; 17629653Sbloom int hbsize; 17729653Sbloom { 17833929Sbostic register u_char *eom, *cp; 17933929Sbostic register int n; 18029653Sbloom HEADER *hp; 18133929Sbostic querybuf answer; 18229653Sbloom u_short type; 18333929Sbostic int first, ancount, qdcount, loopcnt; 18433929Sbostic char nbuf[PACKETSZ]; 18529653Sbloom 18633929Sbostic loopcnt = 0; 18733929Sbostic loop: 18836483Sbostic /* 18936483Sbostic * Use query type of ANY if possible (NO_WILDCARD_MX), which will 19036483Sbostic * find types CNAME, A, and MX, and will cause all existing records 19136483Sbostic * to be cached by our local server. If there is (might be) a 19236483Sbostic * wildcard MX record in the local domain or its parents that are 19336483Sbostic * searched, we can't use ANY; it would cause fully-qualified names 19436483Sbostic * to match as names in a local domain. 19536483Sbostic */ 19636483Sbostic # ifdef NO_WILDCARD_MX 19736483Sbostic n = res_search(host, C_IN, T_ANY, (char *)&answer, sizeof(answer)); 19836483Sbostic # else 19934158Sbostic n = res_search(host, C_IN, T_CNAME, (char *)&answer, sizeof(answer)); 20036483Sbostic # endif 20129653Sbloom if (n < 0) { 20229653Sbloom #ifdef DEBUG 20333929Sbostic if (tTd(8, 1)) 20433929Sbostic printf("getcanonname: res_search failed (errno=%d, h_errno=%d)\n", 20533929Sbostic errno, h_errno); 20629653Sbloom #endif 20729653Sbloom return; 20829653Sbloom } 20933929Sbostic 21033929Sbostic /* find first satisfactory answer */ 21133929Sbostic hp = (HEADER *)&answer; 21229653Sbloom ancount = ntohs(hp->ancount); 21333929Sbostic 21433929Sbostic /* we don't care about errors here, only if we got an answer */ 21529653Sbloom if (ancount == 0) { 21629653Sbloom #ifdef DEBUG 21733929Sbostic if (tTd(8, 1)) 21829653Sbloom printf("rcode = %d, ancount=%d\n", hp->rcode, ancount); 21929653Sbloom #endif 22029653Sbloom return; 22129653Sbloom } 22233929Sbostic cp = (u_char *)&answer + sizeof(HEADER); 22333929Sbostic eom = (u_char *)&answer + n; 22433929Sbostic for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 22533929Sbostic if ((n = dn_skipname(cp, eom)) < 0) 22633929Sbostic return; 22733929Sbostic 22833929Sbostic /* 22933929Sbostic * just in case someone puts a CNAME record after another record, 23033929Sbostic * check all records for CNAME; otherwise, just take the first 23133929Sbostic * name found. 23233929Sbostic */ 23333929Sbostic for (first = 1; --ancount >= 0 && cp < eom; cp += n) { 23429653Sbloom if ((n = dn_expand((char *)&answer, eom, cp, nbuf, 23529653Sbloom sizeof(nbuf))) < 0) 23629653Sbloom break; 23733929Sbostic if (first) { /* XXX */ 23829653Sbloom (void)strncpy(host, nbuf, hbsize); 23929653Sbloom host[hbsize - 1] = '\0'; 24029653Sbloom first = 0; 24129653Sbloom } 24229653Sbloom cp += n; 24333929Sbostic GETSHORT(type, cp); 24429653Sbloom cp += sizeof(u_short) + sizeof(u_long); 24533929Sbostic GETSHORT(n, cp); 24629653Sbloom if (type == T_CNAME) { 24729653Sbloom /* 24833929Sbostic * assume that only one cname will be found. More 24933929Sbostic * than one is undefined. Copy so that if dn_expand 25033929Sbostic * fails, `host' is still okay. 25129653Sbloom */ 25229653Sbloom if ((n = dn_expand((char *)&answer, eom, cp, nbuf, 25329653Sbloom sizeof(nbuf))) < 0) 25429653Sbloom break; 25533929Sbostic (void)strncpy(host, nbuf, hbsize); /* XXX */ 25629653Sbloom host[hbsize - 1] = '\0'; 25733929Sbostic if (++loopcnt > 8) /* never be more than 1 */ 25833929Sbostic return; 25933929Sbostic goto loop; 26029653Sbloom } 26129653Sbloom } 26229653Sbloom } 26335653Seric 264*36494Sphil #else /* not NAMED_BIND */ 265*36494Sphil 266*36494Sphil #include <netdb.h> 267*36494Sphil 268*36494Sphil getcanonname(host, hbsize) 269*36494Sphil char *host; 270*36494Sphil int hbsize; 271*36494Sphil { 272*36494Sphil struct hostent *hp; 273*36494Sphil 274*36494Sphil hp = gethostbyname(host); 275*36494Sphil if (hp == NULL) 276*36494Sphil return; 277*36494Sphil 278*36494Sphil if (strlen(hp->h_name) >= hbsize) 279*36494Sphil return; 280*36494Sphil 281*36494Sphil (void) strcpy(host, hp->h_name); 282*36494Sphil } 283*36494Sphil 284*36494Sphil #endif /* not NAMED_BIND */ 285