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 int err; 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 err = getaddrinfo(host, servname, &hints, &res0); 80 if (err) 81 return (err == EAI_AGAIN ? 1 : -1); 82 83 for (res = res0; res != NULL; res = res->ai_next) { 84 if (*ps + 1 >= roundup(*ps, count_inc)) { 85 size_t newsz = roundup(*ps + 2, count_inc); 86 *he = reallocf(*he, newsz * sizeof(**he)); 87 if (*he == NULL) 88 goto out; 89 } 90 91 p = &(*he)[*ps]; 92 strlcpy(p->host, host, sizeof(p->host)); 93 p->pref = pref; 94 p->ai = *res; 95 p->ai.ai_addr = NULL; 96 bcopy(res->ai_addr, &p->sa, p->ai.ai_addrlen); 97 98 getnameinfo((struct sockaddr *)&p->sa, p->ai.ai_addrlen, 99 p->addr, sizeof(p->addr), 100 NULL, 0, NI_NUMERICHOST); 101 102 (*ps)++; 103 } 104 freeaddrinfo(res0); 105 106 return (0); 107 108 out: 109 if (res0 != NULL) 110 freeaddrinfo(res0); 111 return (1); 112 } 113 114 int 115 dns_get_mx_list(const char *host, int port, struct mx_hostentry **he, int no_mx) 116 { 117 char outname[MAXDNAME]; 118 ns_msg msg; 119 ns_rr rr; 120 const char *searchhost; 121 const unsigned char *cp; 122 unsigned char *ans; 123 struct mx_hostentry *hosts = NULL; 124 size_t nhosts = 0; 125 size_t anssz; 126 int pref; 127 int cname_recurse; 128 int have_mx = 0; 129 int err; 130 int i; 131 132 res_init(); 133 searchhost = host; 134 cname_recurse = 0; 135 136 anssz = 65536; 137 ans = malloc(anssz); 138 if (ans == NULL) 139 return (1); 140 141 if (no_mx) 142 goto out; 143 144 repeat: 145 err = res_search(searchhost, ns_c_in, ns_t_mx, ans, anssz); 146 if (err < 0) { 147 switch (h_errno) { 148 case NO_DATA: 149 /* 150 * Host exists, but no MX (or CNAME) entry. 151 * Not an error, use host name instead. 152 */ 153 goto out; 154 case TRY_AGAIN: 155 /* transient error */ 156 goto transerr; 157 case NO_RECOVERY: 158 case HOST_NOT_FOUND: 159 default: 160 errno = ENOENT; 161 goto err; 162 } 163 } 164 165 if (!ns_initparse(ans, anssz, &msg)) 166 goto transerr; 167 168 switch (ns_msg_getflag(msg, ns_f_rcode)) { 169 case ns_r_noerror: 170 break; 171 case ns_r_nxdomain: 172 goto err; 173 default: 174 goto transerr; 175 } 176 177 for (i = 0; i < ns_msg_count(msg, ns_s_an); i++) { 178 if (ns_parserr(&msg, ns_s_an, i, &rr)) 179 goto transerr; 180 181 cp = ns_rr_rdata(rr); 182 183 switch (ns_rr_type(rr)) { 184 case ns_t_mx: 185 have_mx = 1; 186 pref = ns_get16(cp); 187 cp += 2; 188 err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg), 189 cp, outname, sizeof(outname)); 190 if (err < 0) 191 goto transerr; 192 193 err = add_host(pref, outname, port, &hosts, &nhosts); 194 if (err == -1) 195 goto err; 196 break; 197 198 case ns_t_cname: 199 err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg), 200 cp, outname, sizeof(outname)); 201 if (err < 0) 202 goto transerr; 203 204 /* Prevent a CNAME loop */ 205 if (cname_recurse++ > 10) 206 goto err; 207 208 searchhost = outname; 209 goto repeat; 210 211 default: 212 break; 213 } 214 } 215 216 out: 217 err = 0; 218 if (0) { 219 transerr: 220 if (nhosts == 0) 221 err = 1; 222 } 223 if (0) { 224 err: 225 err = -1; 226 } 227 228 free(ans); 229 230 if (err == 0) { 231 if (!have_mx) { 232 /* 233 * If we didn't find any MX, use the hostname instead. 234 */ 235 err = add_host(0, host, port, &hosts, &nhosts); 236 } else if (nhosts == 0) { 237 /* 238 * We did get MX, but couldn't resolve any of them 239 * due to transient errors. 240 */ 241 err = 1; 242 } 243 } 244 245 if (nhosts > 0) { 246 qsort(hosts, nhosts, sizeof(*hosts), sort_pref); 247 /* terminate list */ 248 *hosts[nhosts].host = 0; 249 } else { 250 if (hosts != NULL) 251 free(hosts); 252 hosts = NULL; 253 } 254 255 *he = hosts; 256 return (err); 257 258 free(ans); 259 if (hosts != NULL) 260 free(hosts); 261 return (err); 262 } 263 264 #if defined(TESTING) 265 int 266 main(int argc, char **argv) 267 { 268 struct mx_hostentry *he, *p; 269 int err; 270 271 err = dns_get_mx_list(argv[1], 53, &he, 0); 272 if (err) 273 return (err); 274 275 for (p = he; *p->host != 0; p++) { 276 printf("%d\t%s\t%s\n", p->pref, p->host, p->addr); 277 } 278 279 return (0); 280 } 281 #endif 282