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