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