5 #include <sys/socket.h>
6 #include <netinet/in.h>
18 #if defined IP_RECVDSTADDR
19 # define DSTADDR_SOCKOPT IP_RECVDSTADDR
20 # define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in_addr)))
21 # define dstaddr(x) (CMSG_DATA(x))
22 #elif defined IP_PKTINFO
24 unsigned int ipi_ifindex; /* Interface index */
25 struct in_addr ipi_spec_dst; /* Local address */
26 struct in_addr ipi_addr; /* Header Destination address */
29 # define DSTADDR_SOCKOPT IP_PKTINFO
30 # define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in_pktinfo)))
31 # define dstaddr(x) (&(((struct in_pktinfo *)(CMSG_DATA(x)))->ipi_addr))
33 # error "can't determine socket option"
38 unsigned char data[DSTADDR_DATASIZE];
59 // -1: premature end of input, forward reference, component > 63 char, invalid character
60 // -2: insufficient space in output
61 int static parse_name(const unsigned char **inpos, const unsigned char *inend, const unsigned char *inbuf, char *buf, size_t bufsize) {
67 // read length of next component
68 int octet = *((*inpos)++);
75 if (bufused == bufsize-1)
81 if ((octet & 0xC0) == 0xC0) {
84 int ref = ((octet - 0xC0) << 8) + *((*inpos)++);
85 if (ref < 0 || ref >= (*inpos)-inbuf) return -1;
86 const unsigned char *newbuf = inbuf + ref;
87 return parse_name(&newbuf, *inpos, inbuf, buf+bufused, bufsize-bufused);
89 if (octet > 63) return -1;
94 if (bufused == bufsize-1)
96 int c = *((*inpos)++);
106 // -1: component > 63 characters
107 // -2: insufficent space in output
108 // -3: two subsequent dots
109 int static write_name(unsigned char** outpos, const unsigned char *outend, const char *name, int offset) {
111 char *dot = strchr(name, '.');
112 const char *fin = dot;
113 if (!dot) fin = name + strlen(name);
114 if (fin - name > 63) return -1;
115 if (fin == name) return -3;
116 if (outend - *outpos < fin - name + 2) return -2;
117 *((*outpos)++) = fin - name;
118 memcpy(*outpos, name, fin - name);
119 *outpos += fin - name;
125 if (outend == *outpos) return -2;
128 if (outend - *outpos < 2) return -2;
129 *((*outpos)++) = (offset >> 8) | 0xC0;
130 *((*outpos)++) = offset & 0xFF;
135 int static write_record(unsigned char** outpos, const unsigned char *outend, const char *name, int offset, dns_type typ, dns_class cls, int ttl) {
136 unsigned char *oldpos = *outpos;
139 int ret = write_name(outpos, outend, name, offset);
140 if (ret) { error = ret; goto error; }
141 if (outend - *outpos < 8) { error = -4; goto error; }
143 *((*outpos)++) = typ >> 8; *((*outpos)++) = typ & 0xFF;
145 *((*outpos)++) = cls >> 8; *((*outpos)++) = cls & 0xFF;
147 *((*outpos)++) = (ttl >> 24) & 0xFF; *((*outpos)++) = (ttl >> 16) & 0xFF; *((*outpos)++) = (ttl >> 8) & 0xFF; *((*outpos)++) = ttl & 0xFF;
155 int static write_record_a(unsigned char** outpos, const unsigned char *outend, const char *name, int offset, dns_class cls, int ttl, const addr_t *ip) {
158 unsigned char *oldpos = *outpos;
160 int ret = write_record(outpos, outend, name, offset, TYPE_A, cls, ttl);
162 if (outend - *outpos < 6) { error = -5; goto error; }
164 *((*outpos)++) = 0; *((*outpos)++) = 4;
166 for (int i=0; i<4; i++)
167 *((*outpos)++) = ip->data.v4[i];
174 int static write_record_aaaa(unsigned char** outpos, const unsigned char *outend, const char *name, int offset, dns_class cls, int ttl, const addr_t *ip) {
177 unsigned char *oldpos = *outpos;
179 int ret = write_record(outpos, outend, name, offset, TYPE_AAAA, cls, ttl);
181 if (outend - *outpos < 6) { error = -5; goto error; }
183 *((*outpos)++) = 0; *((*outpos)++) = 16;
185 for (int i=0; i<16; i++)
186 *((*outpos)++) = ip->data.v6[i];
193 int static write_record_ns(unsigned char** outpos, const unsigned char *outend, char *name, int offset, dns_class cls, int ttl, const char *ns) {
194 unsigned char *oldpos = *outpos;
195 int ret = write_record(outpos, outend, name, offset, TYPE_NS, cls, ttl);
198 if (outend - *outpos < 2) { error = -5; goto error; }
200 unsigned char *curpos = *outpos;
201 ret = write_name(outpos, outend, ns, -1);
202 if (ret) { error = ret; goto error; }
203 curpos[-2] = (*outpos - curpos) >> 8;
204 curpos[-1] = (*outpos - curpos) & 0xFF;
211 int static write_record_soa(unsigned char** outpos, const unsigned char *outend, char *name, int offset, dns_class cls, int ttl, const char* mname, const char *rname,
212 uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum) {
213 unsigned char *oldpos = *outpos;
214 int ret = write_record(outpos, outend, name, offset, TYPE_SOA, cls, ttl);
217 if (outend - *outpos < 2) { error = -5; goto error; }
219 unsigned char *curpos = *outpos;
220 ret = write_name(outpos, outend, mname, -1);
221 if (ret) { error = ret; goto error; }
222 ret = write_name(outpos, outend, rname, -1);
223 if (ret) { error = ret; goto error; }
224 if (outend - *outpos < 20) { error = -5; goto error; }
225 *((*outpos)++) = (serial >> 24) & 0xFF; *((*outpos)++) = (serial >> 16) & 0xFF; *((*outpos)++) = (serial >> 8) & 0xFF; *((*outpos)++) = serial & 0xFF;
226 *((*outpos)++) = (refresh >> 24) & 0xFF; *((*outpos)++) = (refresh >> 16) & 0xFF; *((*outpos)++) = (refresh >> 8) & 0xFF; *((*outpos)++) = refresh & 0xFF;
227 *((*outpos)++) = (retry >> 24) & 0xFF; *((*outpos)++) = (retry >> 16) & 0xFF; *((*outpos)++) = (retry >> 8) & 0xFF; *((*outpos)++) = retry & 0xFF;
228 *((*outpos)++) = (expire >> 24) & 0xFF; *((*outpos)++) = (expire >> 16) & 0xFF; *((*outpos)++) = (expire >> 8) & 0xFF; *((*outpos)++) = expire & 0xFF;
229 *((*outpos)++) = (minimum >> 24) & 0xFF; *((*outpos)++) = (minimum >> 16) & 0xFF; *((*outpos)++) = (minimum >> 8) & 0xFF; *((*outpos)++) = minimum & 0xFF;
230 curpos[-2] = (*outpos - curpos) >> 8;
231 curpos[-1] = (*outpos - curpos) & 0xFF;
238 ssize_t static dnshandle(dns_opt_t *opt, const unsigned char *inbuf, size_t insize, unsigned char* outbuf) {
240 if (insize < 12) // DNS header
243 outbuf[0] = inbuf[0];
244 outbuf[1] = inbuf[1];
246 outbuf[2] = inbuf[2];
247 outbuf[3] = inbuf[3];
251 if (inbuf[2] & 128) { /* printf("Got response?\n"); */ error = 1; goto error; }
253 if (((inbuf[2] & 120) >> 3) != 0) { /* printf("Opcode nonzero?\n"); */ error = 4; goto error; }
255 if (((inbuf[3] & 112) >> 4) != 0) { /* printf("Z nonzero?\n"); */ error = 1; goto error; }
261 int nquestion = (inbuf[4] << 8) + inbuf[5];
262 if (nquestion == 0) { /* printf("No questions?\n"); */ error = 0; goto error; }
263 if (nquestion > 1) { /* printf("Multiple questions %i?\n", nquestion); */ error = 4; goto error; }
264 const unsigned char *inpos = inbuf + 12;
265 const unsigned char *inend = inbuf + insize;
267 int offset = inpos - inbuf;
268 int ret = parse_name(&inpos, inend, inbuf, name, 256);
269 if (ret == -1) { error = 1; goto error; }
270 if (ret == -2) { error = 5; goto error; }
271 int namel = strlen(name), hostl = strlen(opt->host);
272 if (strcmp(name, opt->host) && (namel<hostl+2 || name[namel-hostl-1]!='.' || strcmp(name+namel-hostl,opt->host))) { error = 5; goto error; }
273 if (inend - inpos < 4) { error = 1; goto error; }
274 // copy question to output
275 memcpy(outbuf+12, inbuf+12, inpos+4 - (inbuf+12));
277 outbuf[4] = 0; outbuf[5] = 1;
278 outbuf[6] = 0; outbuf[7] = 0;
279 outbuf[8] = 0; outbuf[9] = 0;
280 outbuf[10] = 0; outbuf[11] = 0;
284 int typ = (inpos[0] << 8) + inpos[1];
285 int cls = (inpos[2] << 8) + inpos[3];
288 unsigned char *outpos = outbuf+(inpos-inbuf);
289 unsigned char *outend = outbuf + BUFLEN;
291 // printf("DNS: Request host='%s' type=%i class=%i\n", name, typ, cls);
293 // calculate size of authority section
297 if (!((typ == TYPE_NS || typ == QTYPE_ANY) && (cls == CLASS_IN || cls == QCLASS_ANY))) {
298 // authority section will be necessary
299 unsigned char *oldpos = outpos;
300 write_record_ns(&oldpos, outend, "", offset, CLASS_IN, 0, opt->ns);
301 auth_size = oldpos - outpos;
302 // printf("Authority section will claim %i bytes\n", auth_size);
310 if ((typ == TYPE_NS || typ == QTYPE_ANY) && (cls == CLASS_IN || cls == QCLASS_ANY)) {
311 int ret2 = write_record_ns(&outpos, outend - auth_size, "", offset, CLASS_IN, opt->nsttl, opt->ns);
312 // printf("wrote NS record: %i\n", ret2);
313 if (!ret2) { outbuf[7]++; have_ns++; }
317 if ((typ == TYPE_SOA || typ == QTYPE_ANY) && (cls == CLASS_IN || cls == QCLASS_ANY) && opt->mbox) {
318 int ret2 = write_record_soa(&outpos, outend - auth_size, "", offset, CLASS_IN, opt->nsttl, opt->ns, opt->mbox, time(NULL), 604800, 86400, 2592000, 604800);
319 // printf("wrote SOA record: %i\n", ret2);
320 if (!ret2) { outbuf[7]++; }
324 if ((typ == TYPE_A || typ == TYPE_AAAA || typ == QTYPE_ANY) && (cls == CLASS_IN || cls == QCLASS_ANY)) {
326 int naddr = opt->cb((void*)opt, addr, 32, typ == TYPE_A || typ == QTYPE_ANY, typ == TYPE_AAAA || typ == QTYPE_ANY);
331 ret = write_record_a(&outpos, outend - auth_size, "", offset, CLASS_IN, opt->datattl, &addr[n]);
332 else if (addr[n].v == 6)
333 ret = write_record_aaaa(&outpos, outend - auth_size, "", offset, CLASS_IN, opt->datattl, &addr[n]);
334 // printf("wrote A record: %i\n", ret);
345 int ret2 = write_record_ns(&outpos, outend, "", offset, CLASS_IN, opt->nsttl, opt->ns);
346 // printf("wrote NS record: %i\n", ret2);
355 return outpos - outbuf;
358 outbuf[3] |= error & 0xF;
360 outbuf[4] = 0; outbuf[5] = 0;
361 outbuf[6] = 0; outbuf[7] = 0;
362 outbuf[8] = 0; outbuf[9] = 0;
363 outbuf[10] = 0; outbuf[11] = 0;
367 static int listenSocket = -1;
369 int dnsserver(dns_opt_t *opt) {
370 struct sockaddr_in si_other;
371 int senderSocket = -1;
372 senderSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
373 if (senderSocket == -1)
377 if (listenSocket == -1) {
378 struct sockaddr_in si_me;
379 if ((listenSocket=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
383 replySocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
384 if (replySocket == -1)
390 setsockopt(listenSocket, IPPROTO_IP, DSTADDR_SOCKOPT, &sockopt, sizeof sockopt);
391 memset((char *) &si_me, 0, sizeof(si_me));
392 si_me.sin_family = AF_INET;
393 si_me.sin_port = htons(opt->port);
394 si_me.sin_addr.s_addr = INADDR_ANY;
395 if (bind(listenSocket, (struct sockaddr*)&si_me, sizeof(si_me))==-1)
399 unsigned char inbuf[BUFLEN], outbuf[BUFLEN];
400 struct iovec iov[1] = {
403 .iov_len = sizeof(inbuf),
406 union control_data cmsg;
407 struct msghdr msg = {
408 .msg_name = &si_other,
409 .msg_namelen = sizeof(si_other),
412 .msg_control = &cmsg,
413 .msg_controllen = sizeof(cmsg),
415 for (; 1; ++(opt->nRequests))
417 ssize_t insize = recvmsg(listenSocket, &msg, 0);
418 unsigned char *addr = (unsigned char*)&si_other.sin_addr.s_addr;
419 // printf("DNS: Request %llu from %i.%i.%i.%i:%i of %i bytes\n", (unsigned long long)(opt->nRequests), addr[0], addr[1], addr[2], addr[3], ntohs(si_other.sin_port), (int)insize);
423 ssize_t ret = dnshandle(opt, inbuf, insize, outbuf);
427 bool handled = false;
428 for (struct cmsghdr*hdr = CMSG_FIRSTHDR(&msg); hdr; hdr = CMSG_NXTHDR(&msg, hdr))
430 if (hdr->cmsg_level == IPPROTO_IP && hdr->cmsg_type == DSTADDR_SOCKOPT)
432 msg.msg_iov[0].iov_base = outbuf;
433 sendmsg(listenSocket, &msg, 0);
434 msg.msg_iov[0].iov_base = inbuf;
439 sendto(listenSocket, outbuf, ret, 0, (struct sockaddr*)&si_other, sizeof(si_other));