1 /* $OpenBSD: dns.c,v 1.10 2009/02/22 11:44:29 form Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/socket.h> 21 #include <sys/queue.h> 22 #include <sys/tree.h> 23 24 #include <netinet/in.h> 25 #include <arpa/inet.h> 26 #include <arpa/nameser.h> 27 28 #include <event.h> 29 #include <netdb.h> 30 #include <resolv.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 35 #include "smtpd.h" 36 37 struct mxrecord { 38 char hostname[MAXHOSTNAMELEN]; 39 u_int16_t priority; 40 }; 41 42 static void mxsort(struct mxrecord *, size_t); 43 44 static void 45 mxsort(struct mxrecord *array, size_t len) 46 { 47 u_int32_t i; 48 int32_t j; 49 struct mxrecord store; 50 51 for (i = 1; i < len; i++) { 52 store = array[i]; 53 for (j = i - 1; j >= 0 && array[j].priority > store.priority; 54 j--) { 55 array[j + 1] = array[j]; 56 } 57 array[j + 1] = store; 58 } 59 } 60 61 int 62 getmxbyname(char *name, char ***result) 63 { 64 union { 65 u_int8_t bytes[PACKETSZ]; 66 HEADER header; 67 } answer; 68 u_int32_t i, j; 69 int ret; 70 u_int8_t *sp; 71 u_int8_t *endp; 72 u_int8_t *ptr; 73 u_int16_t qdcount; 74 u_int8_t expbuf[PACKETSZ]; 75 u_int16_t type; 76 u_int16_t n; 77 u_int16_t priority; 78 size_t mxnb; 79 struct mxrecord mxarray[MXARRAYSIZE]; 80 size_t chunklen; 81 82 ret = res_query(name, C_IN, T_MX, (u_int8_t *)&answer.bytes, 83 sizeof answer); 84 if (ret == -1) { 85 switch (h_errno) { 86 case TRY_AGAIN: 87 return (EAI_AGAIN); 88 case NO_RECOVERY: 89 return (EAI_FAIL); 90 case HOST_NOT_FOUND: 91 return (EAI_NONAME); 92 case NO_DATA: 93 return (0); 94 } 95 fatal("getmxbyname: res_query"); 96 } 97 98 /* sp stores start of dns packet, 99 * endp stores end of dns packet, 100 */ 101 sp = (u_int8_t *)&answer.bytes; 102 endp = sp + ret; 103 104 /* skip header */ 105 ptr = sp + HFIXEDSZ; 106 107 for (qdcount = ntohs(answer.header.qdcount); 108 qdcount--; 109 ptr += ret + QFIXEDSZ) { 110 ret = dn_skipname(ptr, endp); 111 if (ret == -1) 112 return 0; 113 } 114 115 mxnb = 0; 116 for (; ptr < endp;) { 117 memset(expbuf, 0, sizeof expbuf); 118 ret = dn_expand(sp, endp, ptr, expbuf, sizeof expbuf); 119 if (ret == -1) 120 break; 121 ptr += ret; 122 123 GETSHORT(type, ptr); 124 ptr += sizeof(u_int16_t) + sizeof(u_int32_t); 125 GETSHORT(n, ptr); 126 127 if (type != T_MX) { 128 ptr += n; 129 continue; 130 } 131 132 GETSHORT(priority, ptr); 133 ret = dn_expand(sp, endp, ptr, expbuf, sizeof expbuf); 134 if (ret == -1) 135 return 0; 136 ptr += ret; 137 138 if (mxnb < sizeof(mxarray) / sizeof(struct mxrecord)) { 139 if (strlcpy(mxarray[mxnb].hostname, expbuf, 140 sizeof(mxarray[mxnb].hostname)) >= 141 sizeof(mxarray[mxnb].hostname)) 142 return 0; 143 mxarray[mxnb].priority = priority; 144 } 145 else { 146 int tprio = 0; 147 148 for (i = j = 0; 149 i < sizeof(mxarray) / sizeof(struct mxrecord); 150 ++i) { 151 if (tprio < mxarray[i].priority) { 152 tprio = mxarray[i].priority; 153 j = i; 154 } 155 } 156 157 if (mxarray[j].priority > priority) { 158 if (strlcpy(mxarray[j].hostname, expbuf, 159 sizeof(mxarray[j].hostname)) >= 160 sizeof(mxarray[j].hostname)) 161 return 0; 162 mxarray[j].priority = priority; 163 } 164 } 165 ++mxnb; 166 } 167 168 if (mxnb == 0) 169 return 0; 170 171 if (mxnb > sizeof(mxarray) / sizeof(struct mxrecord)) 172 mxnb = sizeof(mxarray) / sizeof(struct mxrecord); 173 174 /* Rearrange MX records by priority */ 175 mxsort(mxarray, mxnb); 176 177 chunklen = 0; 178 for (i = 0; i < mxnb; ++i) 179 chunklen += strlen(mxarray[i].hostname) + 1; 180 chunklen += ((mxnb + 1) * sizeof(char *)); 181 182 *result = calloc(1, chunklen); 183 if (*result == NULL) 184 fatal("getmxbyname: calloc"); 185 186 ptr = (u_int8_t *)*result + (mxnb + 1) * sizeof(char *); 187 for (i = 0; i < mxnb; ++i) { 188 strlcpy(ptr, mxarray[i].hostname, 189 strlen(mxarray[i].hostname) + 1); 190 (*result)[i] = ptr; 191 ptr += strlen(mxarray[i].hostname) + 1; 192 } 193 (*result)[i] = NULL; 194 195 return mxnb; 196 } 197