1*a9e8641dSBaptiste Daroussin /* 2*a9e8641dSBaptiste Daroussin * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3*a9e8641dSBaptiste Daroussin * 4*a9e8641dSBaptiste Daroussin * This code is derived from software contributed to The DragonFly Project 5*a9e8641dSBaptiste Daroussin * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de> 6*a9e8641dSBaptiste Daroussin * 7*a9e8641dSBaptiste Daroussin * Redistribution and use in source and binary forms, with or without 8*a9e8641dSBaptiste Daroussin * modification, are permitted provided that the following conditions 9*a9e8641dSBaptiste Daroussin * are met: 10*a9e8641dSBaptiste Daroussin * 11*a9e8641dSBaptiste Daroussin * 1. Redistributions of source code must retain the above copyright 12*a9e8641dSBaptiste Daroussin * notice, this list of conditions and the following disclaimer. 13*a9e8641dSBaptiste Daroussin * 2. Redistributions in binary form must reproduce the above copyright 14*a9e8641dSBaptiste Daroussin * notice, this list of conditions and the following disclaimer in 15*a9e8641dSBaptiste Daroussin * the documentation and/or other materials provided with the 16*a9e8641dSBaptiste Daroussin * distribution. 17*a9e8641dSBaptiste Daroussin * 3. Neither the name of The DragonFly Project nor the names of its 18*a9e8641dSBaptiste Daroussin * contributors may be used to endorse or promote products derived 19*a9e8641dSBaptiste Daroussin * from this software without specific, prior written permission. 20*a9e8641dSBaptiste Daroussin * 21*a9e8641dSBaptiste Daroussin * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22*a9e8641dSBaptiste Daroussin * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23*a9e8641dSBaptiste Daroussin * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24*a9e8641dSBaptiste Daroussin * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25*a9e8641dSBaptiste Daroussin * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26*a9e8641dSBaptiste Daroussin * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27*a9e8641dSBaptiste Daroussin * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28*a9e8641dSBaptiste Daroussin * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29*a9e8641dSBaptiste Daroussin * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30*a9e8641dSBaptiste Daroussin * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31*a9e8641dSBaptiste Daroussin * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32*a9e8641dSBaptiste Daroussin * SUCH DAMAGE. 33*a9e8641dSBaptiste Daroussin */ 34*a9e8641dSBaptiste Daroussin 35*a9e8641dSBaptiste Daroussin #include <sys/types.h> 36*a9e8641dSBaptiste Daroussin #include <netinet/in.h> 37*a9e8641dSBaptiste Daroussin #include <arpa/inet.h> 38*a9e8641dSBaptiste Daroussin #include <arpa/nameser.h> 39*a9e8641dSBaptiste Daroussin #include <errno.h> 40*a9e8641dSBaptiste Daroussin #include <netdb.h> 41*a9e8641dSBaptiste Daroussin #include <resolv.h> 42*a9e8641dSBaptiste Daroussin #include <string.h> 43*a9e8641dSBaptiste Daroussin #include <stdlib.h> 44*a9e8641dSBaptiste Daroussin 45*a9e8641dSBaptiste Daroussin #include "dma.h" 46*a9e8641dSBaptiste Daroussin 47*a9e8641dSBaptiste Daroussin static int 48*a9e8641dSBaptiste Daroussin sort_pref(const void *a, const void *b) 49*a9e8641dSBaptiste Daroussin { 50*a9e8641dSBaptiste Daroussin const struct mx_hostentry *ha = a, *hb = b; 51*a9e8641dSBaptiste Daroussin int v; 52*a9e8641dSBaptiste Daroussin 53*a9e8641dSBaptiste Daroussin /* sort increasing by preference primarily */ 54*a9e8641dSBaptiste Daroussin v = ha->pref - hb->pref; 55*a9e8641dSBaptiste Daroussin if (v != 0) 56*a9e8641dSBaptiste Daroussin return (v); 57*a9e8641dSBaptiste Daroussin 58*a9e8641dSBaptiste Daroussin /* sort PF_INET6 before PF_INET */ 59*a9e8641dSBaptiste Daroussin v = - (ha->ai.ai_family - hb->ai.ai_family); 60*a9e8641dSBaptiste Daroussin return (v); 61*a9e8641dSBaptiste Daroussin } 62*a9e8641dSBaptiste Daroussin 63*a9e8641dSBaptiste Daroussin static int 64*a9e8641dSBaptiste Daroussin add_host(int pref, const char *host, int port, struct mx_hostentry **he, size_t *ps) 65*a9e8641dSBaptiste Daroussin { 66*a9e8641dSBaptiste Daroussin struct addrinfo hints, *res, *res0 = NULL; 67*a9e8641dSBaptiste Daroussin char servname[10]; 68*a9e8641dSBaptiste Daroussin struct mx_hostentry *p; 69*a9e8641dSBaptiste Daroussin const int count_inc = 10; 70*a9e8641dSBaptiste Daroussin int err; 71*a9e8641dSBaptiste Daroussin 72*a9e8641dSBaptiste Daroussin memset(&hints, 0, sizeof(hints)); 73*a9e8641dSBaptiste Daroussin hints.ai_family = PF_UNSPEC; 74*a9e8641dSBaptiste Daroussin hints.ai_socktype = SOCK_STREAM; 75*a9e8641dSBaptiste Daroussin hints.ai_protocol = IPPROTO_TCP; 76*a9e8641dSBaptiste Daroussin 77*a9e8641dSBaptiste Daroussin snprintf(servname, sizeof(servname), "%d", port); 78*a9e8641dSBaptiste Daroussin err = getaddrinfo(host, servname, &hints, &res0); 79*a9e8641dSBaptiste Daroussin if (err) 80*a9e8641dSBaptiste Daroussin return (err == EAI_AGAIN ? 1 : -1); 81*a9e8641dSBaptiste Daroussin 82*a9e8641dSBaptiste Daroussin for (res = res0; res != NULL; res = res->ai_next) { 83*a9e8641dSBaptiste Daroussin if (*ps + 1 >= roundup(*ps, count_inc)) { 84*a9e8641dSBaptiste Daroussin size_t newsz = roundup(*ps + 2, count_inc); 85*a9e8641dSBaptiste Daroussin *he = reallocf(*he, newsz * sizeof(**he)); 86*a9e8641dSBaptiste Daroussin if (*he == NULL) 87*a9e8641dSBaptiste Daroussin goto out; 88*a9e8641dSBaptiste Daroussin } 89*a9e8641dSBaptiste Daroussin 90*a9e8641dSBaptiste Daroussin p = &(*he)[*ps]; 91*a9e8641dSBaptiste Daroussin strlcpy(p->host, host, sizeof(p->host)); 92*a9e8641dSBaptiste Daroussin p->pref = pref; 93*a9e8641dSBaptiste Daroussin p->ai = *res; 94*a9e8641dSBaptiste Daroussin p->ai.ai_addr = NULL; 95*a9e8641dSBaptiste Daroussin bcopy(res->ai_addr, &p->sa, p->ai.ai_addrlen); 96*a9e8641dSBaptiste Daroussin 97*a9e8641dSBaptiste Daroussin getnameinfo((struct sockaddr *)&p->sa, p->ai.ai_addrlen, 98*a9e8641dSBaptiste Daroussin p->addr, sizeof(p->addr), 99*a9e8641dSBaptiste Daroussin NULL, 0, NI_NUMERICHOST); 100*a9e8641dSBaptiste Daroussin 101*a9e8641dSBaptiste Daroussin (*ps)++; 102*a9e8641dSBaptiste Daroussin } 103*a9e8641dSBaptiste Daroussin freeaddrinfo(res0); 104*a9e8641dSBaptiste Daroussin 105*a9e8641dSBaptiste Daroussin return (0); 106*a9e8641dSBaptiste Daroussin 107*a9e8641dSBaptiste Daroussin out: 108*a9e8641dSBaptiste Daroussin if (res0 != NULL) 109*a9e8641dSBaptiste Daroussin freeaddrinfo(res0); 110*a9e8641dSBaptiste Daroussin return (1); 111*a9e8641dSBaptiste Daroussin } 112*a9e8641dSBaptiste Daroussin 113*a9e8641dSBaptiste Daroussin int 114*a9e8641dSBaptiste Daroussin dns_get_mx_list(const char *host, int port, struct mx_hostentry **he, int no_mx) 115*a9e8641dSBaptiste Daroussin { 116*a9e8641dSBaptiste Daroussin char outname[MAXDNAME]; 117*a9e8641dSBaptiste Daroussin ns_msg msg; 118*a9e8641dSBaptiste Daroussin ns_rr rr; 119*a9e8641dSBaptiste Daroussin const char *searchhost; 120*a9e8641dSBaptiste Daroussin const unsigned char *cp; 121*a9e8641dSBaptiste Daroussin unsigned char *ans; 122*a9e8641dSBaptiste Daroussin struct mx_hostentry *hosts = NULL; 123*a9e8641dSBaptiste Daroussin size_t nhosts = 0; 124*a9e8641dSBaptiste Daroussin size_t anssz; 125*a9e8641dSBaptiste Daroussin int pref; 126*a9e8641dSBaptiste Daroussin int cname_recurse; 127*a9e8641dSBaptiste Daroussin int have_mx = 0; 128*a9e8641dSBaptiste Daroussin int err; 129*a9e8641dSBaptiste Daroussin int i; 130*a9e8641dSBaptiste Daroussin 131*a9e8641dSBaptiste Daroussin res_init(); 132*a9e8641dSBaptiste Daroussin searchhost = host; 133*a9e8641dSBaptiste Daroussin cname_recurse = 0; 134*a9e8641dSBaptiste Daroussin 135*a9e8641dSBaptiste Daroussin anssz = 65536; 136*a9e8641dSBaptiste Daroussin ans = malloc(anssz); 137*a9e8641dSBaptiste Daroussin if (ans == NULL) 138*a9e8641dSBaptiste Daroussin return (1); 139*a9e8641dSBaptiste Daroussin 140*a9e8641dSBaptiste Daroussin if (no_mx) 141*a9e8641dSBaptiste Daroussin goto out; 142*a9e8641dSBaptiste Daroussin 143*a9e8641dSBaptiste Daroussin repeat: 144*a9e8641dSBaptiste Daroussin err = res_search(searchhost, ns_c_in, ns_t_mx, ans, anssz); 145*a9e8641dSBaptiste Daroussin if (err < 0) { 146*a9e8641dSBaptiste Daroussin switch (h_errno) { 147*a9e8641dSBaptiste Daroussin case NO_DATA: 148*a9e8641dSBaptiste Daroussin /* 149*a9e8641dSBaptiste Daroussin * Host exists, but no MX (or CNAME) entry. 150*a9e8641dSBaptiste Daroussin * Not an error, use host name instead. 151*a9e8641dSBaptiste Daroussin */ 152*a9e8641dSBaptiste Daroussin goto out; 153*a9e8641dSBaptiste Daroussin case TRY_AGAIN: 154*a9e8641dSBaptiste Daroussin /* transient error */ 155*a9e8641dSBaptiste Daroussin goto transerr; 156*a9e8641dSBaptiste Daroussin case NO_RECOVERY: 157*a9e8641dSBaptiste Daroussin case HOST_NOT_FOUND: 158*a9e8641dSBaptiste Daroussin default: 159*a9e8641dSBaptiste Daroussin errno = ENOENT; 160*a9e8641dSBaptiste Daroussin goto err; 161*a9e8641dSBaptiste Daroussin } 162*a9e8641dSBaptiste Daroussin } 163*a9e8641dSBaptiste Daroussin 164*a9e8641dSBaptiste Daroussin if (!ns_initparse(ans, anssz, &msg)) 165*a9e8641dSBaptiste Daroussin goto transerr; 166*a9e8641dSBaptiste Daroussin 167*a9e8641dSBaptiste Daroussin switch (ns_msg_getflag(msg, ns_f_rcode)) { 168*a9e8641dSBaptiste Daroussin case ns_r_noerror: 169*a9e8641dSBaptiste Daroussin break; 170*a9e8641dSBaptiste Daroussin case ns_r_nxdomain: 171*a9e8641dSBaptiste Daroussin goto err; 172*a9e8641dSBaptiste Daroussin default: 173*a9e8641dSBaptiste Daroussin goto transerr; 174*a9e8641dSBaptiste Daroussin } 175*a9e8641dSBaptiste Daroussin 176*a9e8641dSBaptiste Daroussin for (i = 0; i < ns_msg_count(msg, ns_s_an); i++) { 177*a9e8641dSBaptiste Daroussin if (ns_parserr(&msg, ns_s_an, i, &rr)) 178*a9e8641dSBaptiste Daroussin goto transerr; 179*a9e8641dSBaptiste Daroussin 180*a9e8641dSBaptiste Daroussin cp = ns_rr_rdata(rr); 181*a9e8641dSBaptiste Daroussin 182*a9e8641dSBaptiste Daroussin switch (ns_rr_type(rr)) { 183*a9e8641dSBaptiste Daroussin case ns_t_mx: 184*a9e8641dSBaptiste Daroussin have_mx = 1; 185*a9e8641dSBaptiste Daroussin pref = ns_get16(cp); 186*a9e8641dSBaptiste Daroussin cp += 2; 187*a9e8641dSBaptiste Daroussin err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg), 188*a9e8641dSBaptiste Daroussin cp, outname, sizeof(outname)); 189*a9e8641dSBaptiste Daroussin if (err < 0) 190*a9e8641dSBaptiste Daroussin goto transerr; 191*a9e8641dSBaptiste Daroussin 192*a9e8641dSBaptiste Daroussin err = add_host(pref, outname, port, &hosts, &nhosts); 193*a9e8641dSBaptiste Daroussin if (err == -1) 194*a9e8641dSBaptiste Daroussin goto err; 195*a9e8641dSBaptiste Daroussin break; 196*a9e8641dSBaptiste Daroussin 197*a9e8641dSBaptiste Daroussin case ns_t_cname: 198*a9e8641dSBaptiste Daroussin err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg), 199*a9e8641dSBaptiste Daroussin cp, outname, sizeof(outname)); 200*a9e8641dSBaptiste Daroussin if (err < 0) 201*a9e8641dSBaptiste Daroussin goto transerr; 202*a9e8641dSBaptiste Daroussin 203*a9e8641dSBaptiste Daroussin /* Prevent a CNAME loop */ 204*a9e8641dSBaptiste Daroussin if (cname_recurse++ > 10) 205*a9e8641dSBaptiste Daroussin goto err; 206*a9e8641dSBaptiste Daroussin 207*a9e8641dSBaptiste Daroussin searchhost = outname; 208*a9e8641dSBaptiste Daroussin goto repeat; 209*a9e8641dSBaptiste Daroussin 210*a9e8641dSBaptiste Daroussin default: 211*a9e8641dSBaptiste Daroussin break; 212*a9e8641dSBaptiste Daroussin } 213*a9e8641dSBaptiste Daroussin } 214*a9e8641dSBaptiste Daroussin 215*a9e8641dSBaptiste Daroussin out: 216*a9e8641dSBaptiste Daroussin err = 0; 217*a9e8641dSBaptiste Daroussin if (0) { 218*a9e8641dSBaptiste Daroussin transerr: 219*a9e8641dSBaptiste Daroussin if (nhosts == 0) 220*a9e8641dSBaptiste Daroussin err = 1; 221*a9e8641dSBaptiste Daroussin } 222*a9e8641dSBaptiste Daroussin if (0) { 223*a9e8641dSBaptiste Daroussin err: 224*a9e8641dSBaptiste Daroussin err = -1; 225*a9e8641dSBaptiste Daroussin } 226*a9e8641dSBaptiste Daroussin 227*a9e8641dSBaptiste Daroussin free(ans); 228*a9e8641dSBaptiste Daroussin 229*a9e8641dSBaptiste Daroussin if (err == 0) { 230*a9e8641dSBaptiste Daroussin if (!have_mx) { 231*a9e8641dSBaptiste Daroussin /* 232*a9e8641dSBaptiste Daroussin * If we didn't find any MX, use the hostname instead. 233*a9e8641dSBaptiste Daroussin */ 234*a9e8641dSBaptiste Daroussin err = add_host(0, host, port, &hosts, &nhosts); 235*a9e8641dSBaptiste Daroussin } else if (nhosts == 0) { 236*a9e8641dSBaptiste Daroussin /* 237*a9e8641dSBaptiste Daroussin * We did get MX, but couldn't resolve any of them 238*a9e8641dSBaptiste Daroussin * due to transient errors. 239*a9e8641dSBaptiste Daroussin */ 240*a9e8641dSBaptiste Daroussin err = 1; 241*a9e8641dSBaptiste Daroussin } 242*a9e8641dSBaptiste Daroussin } 243*a9e8641dSBaptiste Daroussin 244*a9e8641dSBaptiste Daroussin if (nhosts > 0) { 245*a9e8641dSBaptiste Daroussin qsort(hosts, nhosts, sizeof(*hosts), sort_pref); 246*a9e8641dSBaptiste Daroussin /* terminate list */ 247*a9e8641dSBaptiste Daroussin *hosts[nhosts].host = 0; 248*a9e8641dSBaptiste Daroussin } else { 249*a9e8641dSBaptiste Daroussin if (hosts != NULL) 250*a9e8641dSBaptiste Daroussin free(hosts); 251*a9e8641dSBaptiste Daroussin hosts = NULL; 252*a9e8641dSBaptiste Daroussin } 253*a9e8641dSBaptiste Daroussin 254*a9e8641dSBaptiste Daroussin *he = hosts; 255*a9e8641dSBaptiste Daroussin return (err); 256*a9e8641dSBaptiste Daroussin 257*a9e8641dSBaptiste Daroussin free(ans); 258*a9e8641dSBaptiste Daroussin if (hosts != NULL) 259*a9e8641dSBaptiste Daroussin free(hosts); 260*a9e8641dSBaptiste Daroussin return (err); 261*a9e8641dSBaptiste Daroussin } 262*a9e8641dSBaptiste Daroussin 263*a9e8641dSBaptiste Daroussin #if defined(TESTING) 264*a9e8641dSBaptiste Daroussin int 265*a9e8641dSBaptiste Daroussin main(int argc, char **argv) 266*a9e8641dSBaptiste Daroussin { 267*a9e8641dSBaptiste Daroussin struct mx_hostentry *he, *p; 268*a9e8641dSBaptiste Daroussin int err; 269*a9e8641dSBaptiste Daroussin 270*a9e8641dSBaptiste Daroussin err = dns_get_mx_list(argv[1], 53, &he, 0); 271*a9e8641dSBaptiste Daroussin if (err) 272*a9e8641dSBaptiste Daroussin return (err); 273*a9e8641dSBaptiste Daroussin 274*a9e8641dSBaptiste Daroussin for (p = he; *p->host != 0; p++) { 275*a9e8641dSBaptiste Daroussin printf("%d\t%s\t%s\n", p->pref, p->host, p->addr); 276*a9e8641dSBaptiste Daroussin } 277*a9e8641dSBaptiste Daroussin 278*a9e8641dSBaptiste Daroussin return (0); 279*a9e8641dSBaptiste Daroussin } 280*a9e8641dSBaptiste Daroussin #endif 281