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