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