xref: /openbsd-src/usr.sbin/smtpd/dns.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
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