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 <netinet/in.h> 38 #include <arpa/inet.h> 39 #include <arpa/nameser.h> 40 #include <errno.h> 41 #include <netdb.h> 42 #include <resolv.h> 43 #include <string.h> 44 #include <stdlib.h> 45 46 #include "dma.h" 47 48 static int 49 sort_pref(const void *a, const void *b) 50 { 51 const struct mx_hostentry *ha = a, *hb = b; 52 int v; 53 54 /* sort increasing by preference primarily */ 55 v = ha->pref - hb->pref; 56 if (v != 0) 57 return (v); 58 59 /* sort PF_INET6 before PF_INET */ 60 v = - (ha->ai.ai_family - hb->ai.ai_family); 61 return (v); 62 } 63 64 static int 65 add_host(int pref, const char *host, int port, struct mx_hostentry **he, size_t *ps) 66 { 67 struct addrinfo hints, *res, *res0 = NULL; 68 char servname[10]; 69 struct mx_hostentry *p; 70 const int count_inc = 10; 71 72 memset(&hints, 0, sizeof(hints)); 73 hints.ai_family = PF_UNSPEC; 74 hints.ai_socktype = SOCK_STREAM; 75 hints.ai_protocol = IPPROTO_TCP; 76 77 snprintf(servname, sizeof(servname), "%d", port); 78 switch (getaddrinfo(host, servname, &hints, &res0)) { 79 case 0: 80 break; 81 case EAI_AGAIN: 82 case EAI_NONAME: 83 /* 84 * EAI_NONAME gets returned for: 85 * SMARTHOST set but DNS server not reachable -> defer 86 * SMARTHOST set but DNS server returns "host does not exist" 87 * -> buggy configuration 88 * -> either defer or bounce would be ok -> defer 89 * MX entry was returned by DNS server but name doesn't resolve 90 * -> hopefully transient situation -> defer 91 * all other DNS problems should have been caught earlier 92 * in dns_get_mx_list(). 93 */ 94 goto out; 95 default: 96 return(-1); 97 } 98 99 for (res = res0; res != NULL; res = res->ai_next) { 100 if (*ps + 1 >= roundup(*ps, count_inc)) { 101 size_t newsz = roundup(*ps + 2, count_inc); 102 *he = reallocf(*he, newsz * sizeof(**he)); 103 if (*he == NULL) 104 goto out; 105 } 106 107 p = &(*he)[*ps]; 108 strlcpy(p->host, host, sizeof(p->host)); 109 p->pref = pref; 110 p->ai = *res; 111 p->ai.ai_addr = NULL; 112 bcopy(res->ai_addr, &p->sa, p->ai.ai_addrlen); 113 114 getnameinfo((struct sockaddr *)&p->sa, p->ai.ai_addrlen, 115 p->addr, sizeof(p->addr), 116 NULL, 0, NI_NUMERICHOST); 117 118 (*ps)++; 119 } 120 freeaddrinfo(res0); 121 122 return (0); 123 124 out: 125 if (res0 != NULL) 126 freeaddrinfo(res0); 127 return (1); 128 } 129 130 int 131 dns_get_mx_list(const char *host, int port, struct mx_hostentry **he, int no_mx) 132 { 133 char outname[MAXDNAME]; 134 ns_msg msg; 135 ns_rr rr; 136 const char *searchhost; 137 const unsigned char *cp; 138 unsigned char *ans; 139 struct mx_hostentry *hosts = NULL; 140 size_t nhosts = 0; 141 size_t anssz; 142 int pref; 143 int cname_recurse; 144 int have_mx = 0; 145 int err; 146 int i; 147 148 res_init(); 149 searchhost = host; 150 cname_recurse = 0; 151 152 anssz = 65536; 153 ans = malloc(anssz); 154 if (ans == NULL) 155 return (1); 156 157 if (no_mx) 158 goto out; 159 160 repeat: 161 err = res_search(searchhost, ns_c_in, ns_t_mx, ans, anssz); 162 if (err < 0) { 163 switch (h_errno) { 164 case NO_DATA: 165 /* 166 * Host exists, but no MX (or CNAME) entry. 167 * Not an error, use host name instead. 168 */ 169 goto out; 170 case TRY_AGAIN: 171 /* transient error */ 172 goto transerr; 173 case NO_RECOVERY: 174 case HOST_NOT_FOUND: 175 default: 176 errno = ENOENT; 177 goto err; 178 } 179 } 180 181 if (!ns_initparse(ans, anssz, &msg)) 182 goto transerr; 183 184 switch (ns_msg_getflag(msg, ns_f_rcode)) { 185 case ns_r_noerror: 186 break; 187 case ns_r_nxdomain: 188 goto err; 189 default: 190 goto transerr; 191 } 192 193 for (i = 0; i < ns_msg_count(msg, ns_s_an); i++) { 194 if (ns_parserr(&msg, ns_s_an, i, &rr)) 195 goto transerr; 196 197 cp = ns_rr_rdata(rr); 198 199 switch (ns_rr_type(rr)) { 200 case ns_t_mx: 201 have_mx = 1; 202 pref = ns_get16(cp); 203 cp += 2; 204 err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg), 205 cp, outname, sizeof(outname)); 206 if (err < 0) 207 goto transerr; 208 209 err = add_host(pref, outname, port, &hosts, &nhosts); 210 if (err == -1) 211 goto err; 212 break; 213 214 case ns_t_cname: 215 err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg), 216 cp, outname, sizeof(outname)); 217 if (err < 0) 218 goto transerr; 219 220 /* Prevent a CNAME loop */ 221 if (cname_recurse++ > 10) 222 goto err; 223 224 searchhost = outname; 225 goto repeat; 226 227 default: 228 break; 229 } 230 } 231 232 out: 233 err = 0; 234 if (0) { 235 transerr: 236 if (nhosts == 0) 237 err = 1; 238 } 239 if (0) { 240 err: 241 err = -1; 242 } 243 244 free(ans); 245 246 if (err == 0) { 247 if (!have_mx) { 248 /* 249 * If we didn't find any MX, use the hostname instead. 250 */ 251 err = add_host(0, host, port, &hosts, &nhosts); 252 } else if (nhosts == 0) { 253 /* 254 * We did get MX, but couldn't resolve any of them 255 * due to transient errors. 256 */ 257 err = 1; 258 } 259 } 260 261 if (nhosts > 0) { 262 qsort(hosts, nhosts, sizeof(*hosts), sort_pref); 263 /* terminate list */ 264 *hosts[nhosts].host = 0; 265 } else { 266 if (hosts != NULL) 267 free(hosts); 268 hosts = NULL; 269 } 270 271 *he = hosts; 272 return (err); 273 274 free(ans); 275 if (hosts != NULL) 276 free(hosts); 277 return (err); 278 } 279 280 #if defined(TESTING) 281 int 282 main(int argc, char **argv) 283 { 284 struct mx_hostentry *he, *p; 285 int err; 286 287 err = dns_get_mx_list(argv[1], 53, &he, 0); 288 if (err) 289 return (err); 290 291 for (p = he; *p->host != 0; p++) { 292 printf("%d\t%s\t%s\n", p->pref, p->host, p->addr); 293 } 294 295 return (0); 296 } 297 #endif 298