1262266Sbapt/* 2289123Sbapt * Copyright (c) 2008-2014, Simon Schubert <2@0x2c.org>. 3262266Sbapt * Copyright (c) 2008 The DragonFly Project. All rights reserved. 4262266Sbapt * 5262266Sbapt * This code is derived from software contributed to The DragonFly Project 6289123Sbapt * by Simon Schubert <2@0x2c.org>. 7262266Sbapt * 8262266Sbapt * Redistribution and use in source and binary forms, with or without 9262266Sbapt * modification, are permitted provided that the following conditions 10262266Sbapt * are met: 11262266Sbapt * 12262266Sbapt * 1. Redistributions of source code must retain the above copyright 13262266Sbapt * notice, this list of conditions and the following disclaimer. 14262266Sbapt * 2. Redistributions in binary form must reproduce the above copyright 15262266Sbapt * notice, this list of conditions and the following disclaimer in 16262266Sbapt * the documentation and/or other materials provided with the 17262266Sbapt * distribution. 18262266Sbapt * 3. Neither the name of The DragonFly Project nor the names of its 19262266Sbapt * contributors may be used to endorse or promote products derived 20262266Sbapt * from this software without specific, prior written permission. 21262266Sbapt * 22262266Sbapt * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23262266Sbapt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24262266Sbapt * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25262266Sbapt * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26262266Sbapt * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27262266Sbapt * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 28262266Sbapt * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29262266Sbapt * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30262266Sbapt * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31262266Sbapt * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32262266Sbapt * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33262266Sbapt * SUCH DAMAGE. 34262266Sbapt */ 35262266Sbapt 36262266Sbapt#include <sys/types.h> 37304587Sbapt#include <sys/param.h> 38262266Sbapt#include <netinet/in.h> 39262266Sbapt#include <arpa/inet.h> 40262266Sbapt#include <arpa/nameser.h> 41262266Sbapt#include <errno.h> 42262266Sbapt#include <netdb.h> 43262266Sbapt#include <resolv.h> 44262266Sbapt#include <string.h> 45262266Sbapt#include <stdlib.h> 46262266Sbapt 47262266Sbapt#include "dma.h" 48262266Sbapt 49262266Sbaptstatic int 50262266Sbaptsort_pref(const void *a, const void *b) 51262266Sbapt{ 52262266Sbapt const struct mx_hostentry *ha = a, *hb = b; 53262266Sbapt int v; 54262266Sbapt 55262266Sbapt /* sort increasing by preference primarily */ 56262266Sbapt v = ha->pref - hb->pref; 57262266Sbapt if (v != 0) 58262266Sbapt return (v); 59262266Sbapt 60262266Sbapt /* sort PF_INET6 before PF_INET */ 61262266Sbapt v = - (ha->ai.ai_family - hb->ai.ai_family); 62262266Sbapt return (v); 63262266Sbapt} 64262266Sbapt 65262266Sbaptstatic int 66262266Sbaptadd_host(int pref, const char *host, int port, struct mx_hostentry **he, size_t *ps) 67262266Sbapt{ 68262266Sbapt struct addrinfo hints, *res, *res0 = NULL; 69262266Sbapt char servname[10]; 70262266Sbapt struct mx_hostentry *p; 71262266Sbapt const int count_inc = 10; 72262266Sbapt 73262266Sbapt memset(&hints, 0, sizeof(hints)); 74262266Sbapt hints.ai_family = PF_UNSPEC; 75262266Sbapt hints.ai_socktype = SOCK_STREAM; 76262266Sbapt hints.ai_protocol = IPPROTO_TCP; 77262266Sbapt 78262266Sbapt snprintf(servname, sizeof(servname), "%d", port); 79289123Sbapt switch (getaddrinfo(host, servname, &hints, &res0)) { 80289123Sbapt case 0: 81289123Sbapt break; 82289123Sbapt case EAI_AGAIN: 83289123Sbapt case EAI_NONAME: 84289123Sbapt /* 85289123Sbapt * EAI_NONAME gets returned for: 86289123Sbapt * SMARTHOST set but DNS server not reachable -> defer 87289123Sbapt * SMARTHOST set but DNS server returns "host does not exist" 88289123Sbapt * -> buggy configuration 89289123Sbapt * -> either defer or bounce would be ok -> defer 90289123Sbapt * MX entry was returned by DNS server but name doesn't resolve 91289123Sbapt * -> hopefully transient situation -> defer 92289123Sbapt * all other DNS problems should have been caught earlier 93289123Sbapt * in dns_get_mx_list(). 94289123Sbapt */ 95289123Sbapt goto out; 96289123Sbapt default: 97289123Sbapt return(-1); 98289123Sbapt } 99262266Sbapt 100262266Sbapt for (res = res0; res != NULL; res = res->ai_next) { 101262266Sbapt if (*ps + 1 >= roundup(*ps, count_inc)) { 102262266Sbapt size_t newsz = roundup(*ps + 2, count_inc); 103262266Sbapt *he = reallocf(*he, newsz * sizeof(**he)); 104262266Sbapt if (*he == NULL) 105262266Sbapt goto out; 106262266Sbapt } 107262266Sbapt 108262266Sbapt p = &(*he)[*ps]; 109262266Sbapt strlcpy(p->host, host, sizeof(p->host)); 110262266Sbapt p->pref = pref; 111262266Sbapt p->ai = *res; 112262266Sbapt p->ai.ai_addr = NULL; 113262266Sbapt bcopy(res->ai_addr, &p->sa, p->ai.ai_addrlen); 114262266Sbapt 115262266Sbapt getnameinfo((struct sockaddr *)&p->sa, p->ai.ai_addrlen, 116262266Sbapt p->addr, sizeof(p->addr), 117262266Sbapt NULL, 0, NI_NUMERICHOST); 118262266Sbapt 119262266Sbapt (*ps)++; 120262266Sbapt } 121262266Sbapt freeaddrinfo(res0); 122262266Sbapt 123262266Sbapt return (0); 124262266Sbapt 125262266Sbaptout: 126262266Sbapt if (res0 != NULL) 127262266Sbapt freeaddrinfo(res0); 128262266Sbapt return (1); 129262266Sbapt} 130262266Sbapt 131262266Sbaptint 132262266Sbaptdns_get_mx_list(const char *host, int port, struct mx_hostentry **he, int no_mx) 133262266Sbapt{ 134262266Sbapt char outname[MAXDNAME]; 135262266Sbapt ns_msg msg; 136262266Sbapt ns_rr rr; 137262266Sbapt const char *searchhost; 138262266Sbapt const unsigned char *cp; 139262266Sbapt unsigned char *ans; 140262266Sbapt struct mx_hostentry *hosts = NULL; 141262266Sbapt size_t nhosts = 0; 142262266Sbapt size_t anssz; 143262266Sbapt int pref; 144262266Sbapt int cname_recurse; 145262266Sbapt int have_mx = 0; 146262266Sbapt int err; 147262266Sbapt int i; 148262266Sbapt 149262266Sbapt res_init(); 150262266Sbapt searchhost = host; 151262266Sbapt cname_recurse = 0; 152262266Sbapt 153262266Sbapt anssz = 65536; 154262266Sbapt ans = malloc(anssz); 155262266Sbapt if (ans == NULL) 156262266Sbapt return (1); 157262266Sbapt 158262266Sbapt if (no_mx) 159262266Sbapt goto out; 160262266Sbapt 161262266Sbaptrepeat: 162262266Sbapt err = res_search(searchhost, ns_c_in, ns_t_mx, ans, anssz); 163262266Sbapt if (err < 0) { 164262266Sbapt switch (h_errno) { 165262266Sbapt case NO_DATA: 166262266Sbapt /* 167262266Sbapt * Host exists, but no MX (or CNAME) entry. 168262266Sbapt * Not an error, use host name instead. 169262266Sbapt */ 170262266Sbapt goto out; 171262266Sbapt case TRY_AGAIN: 172262266Sbapt /* transient error */ 173262266Sbapt goto transerr; 174262266Sbapt case NO_RECOVERY: 175262266Sbapt case HOST_NOT_FOUND: 176262266Sbapt default: 177262266Sbapt errno = ENOENT; 178262266Sbapt goto err; 179262266Sbapt } 180262266Sbapt } 181262266Sbapt 182262266Sbapt if (!ns_initparse(ans, anssz, &msg)) 183262266Sbapt goto transerr; 184262266Sbapt 185262266Sbapt switch (ns_msg_getflag(msg, ns_f_rcode)) { 186262266Sbapt case ns_r_noerror: 187262266Sbapt break; 188262266Sbapt case ns_r_nxdomain: 189262266Sbapt goto err; 190262266Sbapt default: 191262266Sbapt goto transerr; 192262266Sbapt } 193262266Sbapt 194262266Sbapt for (i = 0; i < ns_msg_count(msg, ns_s_an); i++) { 195262266Sbapt if (ns_parserr(&msg, ns_s_an, i, &rr)) 196262266Sbapt goto transerr; 197262266Sbapt 198262266Sbapt cp = ns_rr_rdata(rr); 199262266Sbapt 200262266Sbapt switch (ns_rr_type(rr)) { 201262266Sbapt case ns_t_mx: 202262266Sbapt have_mx = 1; 203262266Sbapt pref = ns_get16(cp); 204262266Sbapt cp += 2; 205262266Sbapt err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg), 206262266Sbapt cp, outname, sizeof(outname)); 207262266Sbapt if (err < 0) 208262266Sbapt goto transerr; 209262266Sbapt 210262266Sbapt err = add_host(pref, outname, port, &hosts, &nhosts); 211262266Sbapt if (err == -1) 212262266Sbapt goto err; 213262266Sbapt break; 214262266Sbapt 215262266Sbapt case ns_t_cname: 216262266Sbapt err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg), 217262266Sbapt cp, outname, sizeof(outname)); 218262266Sbapt if (err < 0) 219262266Sbapt goto transerr; 220262266Sbapt 221262266Sbapt /* Prevent a CNAME loop */ 222262266Sbapt if (cname_recurse++ > 10) 223262266Sbapt goto err; 224262266Sbapt 225262266Sbapt searchhost = outname; 226262266Sbapt goto repeat; 227262266Sbapt 228262266Sbapt default: 229262266Sbapt break; 230262266Sbapt } 231262266Sbapt } 232262266Sbapt 233262266Sbaptout: 234262266Sbapt err = 0; 235262266Sbapt if (0) { 236262266Sbapttranserr: 237262266Sbapt if (nhosts == 0) 238262266Sbapt err = 1; 239262266Sbapt } 240262266Sbapt if (0) { 241262266Sbapterr: 242262266Sbapt err = -1; 243262266Sbapt } 244262266Sbapt 245262266Sbapt free(ans); 246262266Sbapt 247262266Sbapt if (err == 0) { 248262266Sbapt if (!have_mx) { 249262266Sbapt /* 250262266Sbapt * If we didn't find any MX, use the hostname instead. 251262266Sbapt */ 252262266Sbapt err = add_host(0, host, port, &hosts, &nhosts); 253262266Sbapt } else if (nhosts == 0) { 254262266Sbapt /* 255262266Sbapt * We did get MX, but couldn't resolve any of them 256262266Sbapt * due to transient errors. 257262266Sbapt */ 258262266Sbapt err = 1; 259262266Sbapt } 260262266Sbapt } 261262266Sbapt 262262266Sbapt if (nhosts > 0) { 263262266Sbapt qsort(hosts, nhosts, sizeof(*hosts), sort_pref); 264262266Sbapt /* terminate list */ 265262266Sbapt *hosts[nhosts].host = 0; 266262266Sbapt } else { 267262266Sbapt if (hosts != NULL) 268262266Sbapt free(hosts); 269262266Sbapt hosts = NULL; 270262266Sbapt } 271262266Sbapt 272262266Sbapt *he = hosts; 273262266Sbapt return (err); 274262266Sbapt 275262266Sbapt free(ans); 276262266Sbapt if (hosts != NULL) 277262266Sbapt free(hosts); 278262266Sbapt return (err); 279262266Sbapt} 280262266Sbapt 281262266Sbapt#if defined(TESTING) 282262266Sbaptint 283262266Sbaptmain(int argc, char **argv) 284262266Sbapt{ 285262266Sbapt struct mx_hostentry *he, *p; 286262266Sbapt int err; 287262266Sbapt 288262266Sbapt err = dns_get_mx_list(argv[1], 53, &he, 0); 289262266Sbapt if (err) 290262266Sbapt return (err); 291262266Sbapt 292262266Sbapt for (p = he; *p->host != 0; p++) { 293262266Sbapt printf("%d\t%s\t%s\n", p->pref, p->host, p->addr); 294262266Sbapt } 295262266Sbapt 296262266Sbapt return (0); 297262266Sbapt} 298262266Sbapt#endif 299