Changeset View
Changeset View
Standalone View
Standalone View
src/seeder/dns.c
#include "dns.h" | |||||
#include <arpa/inet.h> | #include <arpa/inet.h> | ||||
#include <ctype.h> | #include <ctype.h> | ||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <stdbool.h> | #include <stdbool.h> | ||||
#include <stdint.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <strings.h> | #include <strings.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <time.h> | #include <time.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include "dns.h" | |||||
#define BUFLEN 512 | #define BUFLEN 512 | ||||
#if defined IP_RECVDSTADDR | #if defined IP_RECVDSTADDR | ||||
#define DSTADDR_SOCKOPT IP_RECVDSTADDR | #define DSTADDR_SOCKOPT IP_RECVDSTADDR | ||||
#define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in6_addr))) | #define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in6_addr))) | ||||
#define dstaddr(x) (CMSG_DATA(x)) | #define dstaddr(x) (CMSG_DATA(x)) | ||||
#elif defined IPV6_PKTINFO | #elif defined IPV6_PKTINFO | ||||
struct in6_pktinfo { | struct in6_pktinfo { | ||||
struct in6_addr ipi6_addr; /* src/dst IPv6 address */ | struct in6_addr ipi6_addr; /* src/dst IPv6 address */ | ||||
unsigned int ipi6_ifindex; /* send/recv interface index */ | unsigned int ipi6_ifindex; /* send/recv interface index */ | ||||
}; | }; | ||||
#define DSTADDR_SOCKOPT IPV6_PKTINFO | #define DSTADDR_SOCKOPT IPV6_PKTINFO | ||||
#define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in6_pktinfo))) | #define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in6_pktinfo))) | ||||
#define dstaddr(x) (&(((struct in6_pktinfo *)(CMSG_DATA(x)))->ipi6_addr)) | #define dstaddr(x) (&(((struct in6_pktinfo *)(CMSG_DATA(x)))->ipi6_addr)) | ||||
#else | #else | ||||
#error "can't determine socket option" | #error "can't determine socket option" | ||||
#endif | #endif | ||||
union control_data { | union control_data { | ||||
struct cmsghdr cmsg; | struct cmsghdr cmsg; | ||||
unsigned char data[DSTADDR_DATASIZE]; | uint8_t data[DSTADDR_DATASIZE]; | ||||
}; | }; | ||||
typedef enum { CLASS_IN = 1, QCLASS_ANY = 255 } dns_class; | typedef enum { CLASS_IN = 1, QCLASS_ANY = 255 } dns_class; | ||||
typedef enum { | typedef enum { | ||||
TYPE_A = 1, | TYPE_A = 1, | ||||
TYPE_NS = 2, | TYPE_NS = 2, | ||||
TYPE_CNAME = 5, | TYPE_CNAME = 5, | ||||
TYPE_SOA = 6, | TYPE_SOA = 6, | ||||
TYPE_MX = 15, | TYPE_MX = 15, | ||||
TYPE_AAAA = 28, | TYPE_AAAA = 28, | ||||
TYPE_SRV = 33, | TYPE_SRV = 33, | ||||
QTYPE_ANY = 255 | QTYPE_ANY = 255 | ||||
} dns_type; | } dns_type; | ||||
// 0: ok | // 0: ok | ||||
// -1: premature end of input, forward reference, component > 63 char, invalid | // -1: premature end of input, forward reference, component > 63 char, invalid | ||||
// character | // character | ||||
// -2: insufficient space in output | // -2: insufficient space in output | ||||
int static parse_name(const unsigned char **inpos, const unsigned char *inend, | static int parse_name(const uint8_t **inpos, const uint8_t *inend, | ||||
const unsigned char *inbuf, char *buf, size_t bufsize) { | const uint8_t *inbuf, char *buf, size_t bufsize) { | ||||
size_t bufused = 0; | size_t bufused = 0; | ||||
int init = 1; | int init = 1; | ||||
do { | do { | ||||
if (*inpos == inend) return -1; | if (*inpos == inend) return -1; | ||||
// read length of next component | // read length of next component | ||||
int octet = *((*inpos)++); | int octet = *((*inpos)++); | ||||
if (octet == 0) { | if (octet == 0) { | ||||
buf[bufused] = 0; | buf[bufused] = 0; | ||||
return 0; | return 0; | ||||
} | } | ||||
// add dot in output | // add dot in output | ||||
if (!init) { | if (!init) { | ||||
if (bufused == bufsize - 1) return -2; | if (bufused == bufsize - 1) return -2; | ||||
buf[bufused++] = '.'; | buf[bufused++] = '.'; | ||||
} else | } else | ||||
init = 0; | init = 0; | ||||
// handle references | // handle references | ||||
if ((octet & 0xC0) == 0xC0) { | if ((octet & 0xC0) == 0xC0) { | ||||
if (*inpos == inend) return -1; | if (*inpos == inend) return -1; | ||||
int ref = ((octet - 0xC0) << 8) + *((*inpos)++); | int ref = ((octet - 0xC0) << 8) + *((*inpos)++); | ||||
if (ref < 0 || ref >= (*inpos) - inbuf - 2) return -1; | if (ref < 0 || ref >= (*inpos) - inbuf - 2) return -1; | ||||
const unsigned char *newbuf = inbuf + ref; | const uint8_t *newbuf = inbuf + ref; | ||||
return parse_name(&newbuf, (*inpos) - 2, inbuf, buf + bufused, | return parse_name(&newbuf, (*inpos) - 2, inbuf, buf + bufused, | ||||
bufsize - bufused); | bufsize - bufused); | ||||
} | } | ||||
if (octet > 63) return -1; | if (octet > 63) return -1; | ||||
// copy label | // copy label | ||||
while (octet) { | while (octet) { | ||||
if (*inpos == inend) return -1; | if (*inpos == inend) return -1; | ||||
if (bufused == bufsize - 1) return -2; | if (bufused == bufsize - 1) return -2; | ||||
int c = *((*inpos)++); | int c = *((*inpos)++); | ||||
if (c == '.') return -1; | if (c == '.') return -1; | ||||
octet--; | octet--; | ||||
buf[bufused++] = c; | buf[bufused++] = c; | ||||
} | } | ||||
} while (1); | } while (1); | ||||
} | } | ||||
// 0: k | // 0: k | ||||
// -1: component > 63 characters | // -1: component > 63 characters | ||||
// -2: insufficent space in output | // -2: insufficent space in output | ||||
// -3: two subsequent dots | // -3: two subsequent dots | ||||
int static write_name(unsigned char **outpos, const unsigned char *outend, | static int write_name(uint8_t **outpos, const uint8_t *outend, const char *name, | ||||
const char *name, int offset) { | int offset) { | ||||
while (*name != 0) { | while (*name != 0) { | ||||
char *dot = strchr(name, '.'); | char *dot = strchr(name, '.'); | ||||
const char *fin = dot; | const char *fin = dot; | ||||
if (!dot) fin = name + strlen(name); | if (!dot) fin = name + strlen(name); | ||||
if (fin - name > 63) return -1; | if (fin - name > 63) return -1; | ||||
if (fin == name) return -3; | if (fin == name) return -3; | ||||
if (outend - *outpos < fin - name + 2) return -2; | if (outend - *outpos < fin - name + 2) return -2; | ||||
*((*outpos)++) = fin - name; | *((*outpos)++) = fin - name; | ||||
Show All 9 Lines | static int write_name(uint8_t **outpos, const uint8_t *outend, const char *name, | ||||
} else { | } else { | ||||
if (outend - *outpos < 2) return -2; | if (outend - *outpos < 2) return -2; | ||||
*((*outpos)++) = (offset >> 8) | 0xC0; | *((*outpos)++) = (offset >> 8) | 0xC0; | ||||
*((*outpos)++) = offset & 0xFF; | *((*outpos)++) = offset & 0xFF; | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
int static write_record(unsigned char **outpos, const unsigned char *outend, | static int write_record(uint8_t **outpos, const uint8_t *outend, | ||||
const char *name, int offset, dns_type typ, | const char *name, int offset, dns_type typ, | ||||
dns_class cls, int ttl) { | dns_class cls, int ttl) { | ||||
unsigned char *oldpos = *outpos; | uint8_t *oldpos = *outpos; | ||||
int error = 0; | int error = 0; | ||||
// name | // name | ||||
int ret = write_name(outpos, outend, name, offset); | int ret = write_name(outpos, outend, name, offset); | ||||
if (ret) { | if (ret) { | ||||
error = ret; | error = ret; | ||||
goto error; | goto error; | ||||
} | } | ||||
if (outend - *outpos < 8) { | if (outend - *outpos < 8) { | ||||
Show All 12 Lines | static int write_record(uint8_t **outpos, const uint8_t *outend, | ||||
*((*outpos)++) = (ttl >> 8) & 0xFF; | *((*outpos)++) = (ttl >> 8) & 0xFF; | ||||
*((*outpos)++) = ttl & 0xFF; | *((*outpos)++) = ttl & 0xFF; | ||||
return 0; | return 0; | ||||
error: | error: | ||||
*outpos = oldpos; | *outpos = oldpos; | ||||
return error; | return error; | ||||
} | } | ||||
int static write_record_a(unsigned char **outpos, const unsigned char *outend, | static int write_record_a(uint8_t **outpos, const uint8_t *outend, | ||||
const char *name, int offset, dns_class cls, int ttl, | const char *name, int offset, dns_class cls, int ttl, | ||||
const addr_t *ip) { | const addr_t *ip) { | ||||
if (ip->v != 4) return -6; | if (ip->v != 4) return -6; | ||||
unsigned char *oldpos = *outpos; | uint8_t *oldpos = *outpos; | ||||
int error = 0; | int error = 0; | ||||
int ret = write_record(outpos, outend, name, offset, TYPE_A, cls, ttl); | int ret = write_record(outpos, outend, name, offset, TYPE_A, cls, ttl); | ||||
if (ret) return ret; | if (ret) return ret; | ||||
if (outend - *outpos < 6) { | if (outend - *outpos < 6) { | ||||
error = -5; | error = -5; | ||||
goto error; | goto error; | ||||
} | } | ||||
// rdlength | // rdlength | ||||
*((*outpos)++) = 0; | *((*outpos)++) = 0; | ||||
*((*outpos)++) = 4; | *((*outpos)++) = 4; | ||||
// rdata | // rdata | ||||
for (int i = 0; i < 4; i++) | for (int i = 0; i < 4; i++) | ||||
*((*outpos)++) = ip->data.v4[i]; | *((*outpos)++) = ip->data.v4[i]; | ||||
return 0; | return 0; | ||||
error: | error: | ||||
*outpos = oldpos; | *outpos = oldpos; | ||||
return error; | return error; | ||||
} | } | ||||
int static write_record_aaaa(unsigned char **outpos, | static int write_record_aaaa(uint8_t **outpos, const uint8_t *outend, | ||||
const unsigned char *outend, const char *name, | const char *name, int offset, dns_class cls, | ||||
int offset, dns_class cls, int ttl, | int ttl, const addr_t *ip) { | ||||
const addr_t *ip) { | |||||
if (ip->v != 6) return -6; | if (ip->v != 6) return -6; | ||||
unsigned char *oldpos = *outpos; | uint8_t *oldpos = *outpos; | ||||
int error = 0; | int error = 0; | ||||
int ret = write_record(outpos, outend, name, offset, TYPE_AAAA, cls, ttl); | int ret = write_record(outpos, outend, name, offset, TYPE_AAAA, cls, ttl); | ||||
if (ret) return ret; | if (ret) return ret; | ||||
if (outend - *outpos < 6) { | if (outend - *outpos < 6) { | ||||
error = -5; | error = -5; | ||||
goto error; | goto error; | ||||
} | } | ||||
// rdlength | // rdlength | ||||
*((*outpos)++) = 0; | *((*outpos)++) = 0; | ||||
*((*outpos)++) = 16; | *((*outpos)++) = 16; | ||||
// rdata | // rdata | ||||
for (int i = 0; i < 16; i++) | for (int i = 0; i < 16; i++) | ||||
*((*outpos)++) = ip->data.v6[i]; | *((*outpos)++) = ip->data.v6[i]; | ||||
return 0; | return 0; | ||||
error: | error: | ||||
*outpos = oldpos; | *outpos = oldpos; | ||||
return error; | return error; | ||||
} | } | ||||
int static write_record_ns(unsigned char **outpos, const unsigned char *outend, | static int write_record_ns(uint8_t **outpos, const uint8_t *outend, char *name, | ||||
char *name, int offset, dns_class cls, int ttl, | int offset, dns_class cls, int ttl, const char *ns) { | ||||
const char *ns) { | uint8_t *oldpos = *outpos; | ||||
unsigned char *oldpos = *outpos; | |||||
int ret = write_record(outpos, outend, name, offset, TYPE_NS, cls, ttl); | int ret = write_record(outpos, outend, name, offset, TYPE_NS, cls, ttl); | ||||
if (ret) return ret; | if (ret) return ret; | ||||
int error = 0; | int error = 0; | ||||
if (outend - *outpos < 2) { | if (outend - *outpos < 2) { | ||||
error = -5; | error = -5; | ||||
goto error; | goto error; | ||||
} | } | ||||
(*outpos) += 2; | (*outpos) += 2; | ||||
unsigned char *curpos = *outpos; | uint8_t *curpos = *outpos; | ||||
ret = write_name(outpos, outend, ns, -1); | ret = write_name(outpos, outend, ns, -1); | ||||
if (ret) { | if (ret) { | ||||
error = ret; | error = ret; | ||||
goto error; | goto error; | ||||
} | } | ||||
curpos[-2] = (*outpos - curpos) >> 8; | curpos[-2] = (*outpos - curpos) >> 8; | ||||
curpos[-1] = (*outpos - curpos) & 0xFF; | curpos[-1] = (*outpos - curpos) & 0xFF; | ||||
return 0; | return 0; | ||||
error: | error: | ||||
*outpos = oldpos; | *outpos = oldpos; | ||||
return error; | return error; | ||||
} | } | ||||
int static write_record_soa(unsigned char **outpos, const unsigned char *outend, | static int write_record_soa(uint8_t **outpos, const uint8_t *outend, char *name, | ||||
char *name, int offset, dns_class cls, int ttl, | int offset, dns_class cls, int ttl, | ||||
const char *mname, const char *rname, | const char *mname, const char *rname, | ||||
uint32_t serial, uint32_t refresh, uint32_t retry, | uint32_t serial, uint32_t refresh, uint32_t retry, | ||||
uint32_t expire, uint32_t minimum) { | uint32_t expire, uint32_t minimum) { | ||||
unsigned char *oldpos = *outpos; | uint8_t *oldpos = *outpos; | ||||
int ret = write_record(outpos, outend, name, offset, TYPE_SOA, cls, ttl); | int ret = write_record(outpos, outend, name, offset, TYPE_SOA, cls, ttl); | ||||
if (ret) return ret; | if (ret) return ret; | ||||
int error = 0; | int error = 0; | ||||
if (outend - *outpos < 2) { | if (outend - *outpos < 2) { | ||||
error = -5; | error = -5; | ||||
goto error; | goto error; | ||||
} | } | ||||
(*outpos) += 2; | (*outpos) += 2; | ||||
unsigned char *curpos = *outpos; | uint8_t *curpos = *outpos; | ||||
ret = write_name(outpos, outend, mname, -1); | ret = write_name(outpos, outend, mname, -1); | ||||
if (ret) { | if (ret) { | ||||
error = ret; | error = ret; | ||||
goto error; | goto error; | ||||
} | } | ||||
ret = write_name(outpos, outend, rname, -1); | ret = write_name(outpos, outend, rname, -1); | ||||
if (ret) { | if (ret) { | ||||
error = ret; | error = ret; | ||||
Show All 26 Lines | static int write_record_soa(uint8_t **outpos, const uint8_t *outend, char *name, | ||||
curpos[-2] = (*outpos - curpos) >> 8; | curpos[-2] = (*outpos - curpos) >> 8; | ||||
curpos[-1] = (*outpos - curpos) & 0xFF; | curpos[-1] = (*outpos - curpos) & 0xFF; | ||||
return 0; | return 0; | ||||
error: | error: | ||||
*outpos = oldpos; | *outpos = oldpos; | ||||
return error; | return error; | ||||
} | } | ||||
ssize_t static dnshandle(dns_opt_t *opt, const unsigned char *inbuf, | static ssize_t dnshandle(dns_opt_t *opt, const uint8_t *inbuf, size_t insize, | ||||
size_t insize, unsigned char *outbuf) { | uint8_t *outbuf) { | ||||
int error = 0; | int error = 0; | ||||
if (insize < 12) // DNS header | if (insize < 12) // DNS header | ||||
return -1; | return -1; | ||||
// copy id | // copy id | ||||
outbuf[0] = inbuf[0]; | outbuf[0] = inbuf[0]; | ||||
outbuf[1] = inbuf[1]; | outbuf[1] = inbuf[1]; | ||||
// copy flags; | // copy flags; | ||||
outbuf[2] = inbuf[2]; | outbuf[2] = inbuf[2]; | ||||
Show All 19 Lines | static ssize_t dnshandle(dns_opt_t *opt, const uint8_t *inbuf, size_t insize, | ||||
if (nquestion == 0) { /* printf("No questions?\n"); */ | if (nquestion == 0) { /* printf("No questions?\n"); */ | ||||
error = 0; | error = 0; | ||||
goto error; | goto error; | ||||
} | } | ||||
if (nquestion > 1) { /* printf("Multiple questions %i?\n", nquestion); */ | if (nquestion > 1) { /* printf("Multiple questions %i?\n", nquestion); */ | ||||
error = 4; | error = 4; | ||||
goto error; | goto error; | ||||
} | } | ||||
const unsigned char *inpos = inbuf + 12; | const uint8_t *inpos = inbuf + 12; | ||||
const unsigned char *inend = inbuf + insize; | const uint8_t *inend = inbuf + insize; | ||||
char name[256]; | char name[256]; | ||||
int offset = inpos - inbuf; | int offset = inpos - inbuf; | ||||
int ret = parse_name(&inpos, inend, inbuf, name, 256); | int ret = parse_name(&inpos, inend, inbuf, name, 256); | ||||
if (ret == -1) { | if (ret == -1) { | ||||
error = 1; | error = 1; | ||||
goto error; | goto error; | ||||
} | } | ||||
if (ret == -2) { | if (ret == -2) { | ||||
Show All 24 Lines | static ssize_t dnshandle(dns_opt_t *opt, const uint8_t *inbuf, size_t insize, | ||||
outbuf[11] = 0; | outbuf[11] = 0; | ||||
// set qr | // set qr | ||||
outbuf[2] |= 128; | outbuf[2] |= 128; | ||||
int typ = (inpos[0] << 8) + inpos[1]; | int typ = (inpos[0] << 8) + inpos[1]; | ||||
int cls = (inpos[2] << 8) + inpos[3]; | int cls = (inpos[2] << 8) + inpos[3]; | ||||
inpos += 4; | inpos += 4; | ||||
unsigned char *outpos = outbuf + (inpos - inbuf); | uint8_t *outpos = outbuf + (inpos - inbuf); | ||||
unsigned char *outend = outbuf + BUFLEN; | uint8_t *outend = outbuf + BUFLEN; | ||||
// printf("DNS: Request host='%s' type=%i class=%i\n", name, typ, cls); | // printf("DNS: Request host='%s' type=%i class=%i\n", name, typ, cls); | ||||
// calculate max size of authority section | // calculate max size of authority section | ||||
int max_auth_size = 0; | int max_auth_size = 0; | ||||
if (!((typ == TYPE_NS || typ == QTYPE_ANY) && | if (!((typ == TYPE_NS || typ == QTYPE_ANY) && | ||||
(cls == CLASS_IN || cls == QCLASS_ANY))) { | (cls == CLASS_IN || cls == QCLASS_ANY))) { | ||||
// authority section will be necessary, either NS or SOA | // authority section will be necessary, either NS or SOA | ||||
unsigned char *newpos = outpos; | uint8_t *newpos = outpos; | ||||
write_record_ns(&newpos, outend, "", offset, CLASS_IN, 0, opt->ns); | write_record_ns(&newpos, outend, "", offset, CLASS_IN, 0, opt->ns); | ||||
max_auth_size = newpos - outpos; | max_auth_size = newpos - outpos; | ||||
newpos = outpos; | newpos = outpos; | ||||
write_record_soa(&newpos, outend, "", offset, CLASS_IN, opt->nsttl, | write_record_soa(&newpos, outend, "", offset, CLASS_IN, opt->nsttl, | ||||
opt->ns, opt->mbox, time(NULL), 604800, 86400, 2592000, | opt->ns, opt->mbox, time(NULL), 604800, 86400, 2592000, | ||||
604800); | 604800); | ||||
if (max_auth_size < newpos - outpos) max_auth_size = newpos - outpos; | if (max_auth_size < newpos - outpos) max_auth_size = newpos - outpos; | ||||
▲ Show 20 Lines • Show All 122 Lines • ▼ Show 20 Lines | if (listenSocket == -1) { | ||||
memset((char *)&si_me, 0, sizeof(si_me)); | memset((char *)&si_me, 0, sizeof(si_me)); | ||||
si_me.sin6_family = AF_INET6; | si_me.sin6_family = AF_INET6; | ||||
si_me.sin6_port = htons(opt->port); | si_me.sin6_port = htons(opt->port); | ||||
si_me.sin6_addr = in6addr_any; | si_me.sin6_addr = in6addr_any; | ||||
if (bind(listenSocket, (struct sockaddr *)&si_me, sizeof(si_me)) == -1) | if (bind(listenSocket, (struct sockaddr *)&si_me, sizeof(si_me)) == -1) | ||||
return -2; | return -2; | ||||
} | } | ||||
unsigned char inbuf[BUFLEN], outbuf[BUFLEN]; | uint8_t inbuf[BUFLEN], outbuf[BUFLEN]; | ||||
struct iovec iov[1] = { | struct iovec iov[1] = { | ||||
{ | { | ||||
.iov_base = inbuf, .iov_len = sizeof(inbuf), | .iov_base = inbuf, .iov_len = sizeof(inbuf), | ||||
}, | }, | ||||
}; | }; | ||||
union control_data cmsg; | union control_data cmsg; | ||||
struct msghdr msg = { | struct msghdr msg = { | ||||
.msg_name = &si_other, | .msg_name = &si_other, | ||||
.msg_namelen = sizeof(si_other), | .msg_namelen = sizeof(si_other), | ||||
.msg_iov = iov, | .msg_iov = iov, | ||||
.msg_iovlen = 1, | .msg_iovlen = 1, | ||||
.msg_control = &cmsg, | .msg_control = &cmsg, | ||||
.msg_controllen = sizeof(cmsg), | .msg_controllen = sizeof(cmsg), | ||||
}; | }; | ||||
for (; 1; ++(opt->nRequests)) { | for (; 1; ++(opt->nRequests)) { | ||||
ssize_t insize = recvmsg(listenSocket, &msg, 0); | ssize_t insize = recvmsg(listenSocket, &msg, 0); | ||||
// unsigned char *addr = (unsigned char*)&si_other.sin_addr.s_addr; | // uint8_t *addr = (uint8_t*)&si_other.sin_addr.s_addr; | ||||
// printf("DNS: Request %llu from %i.%i.%i.%i:%i of %i bytes\n", | // 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], | // (unsigned long long)(opt->nRequests), addr[0], addr[1], addr[2], | ||||
// addr[3], ntohs(si_other.sin_port), (int)insize); | // addr[3], ntohs(si_other.sin_port), (int)insize); | ||||
if (insize <= 0) continue; | if (insize <= 0) continue; | ||||
ssize_t ret = dnshandle(opt, inbuf, insize, outbuf); | ssize_t ret = dnshandle(opt, inbuf, insize, outbuf); | ||||
if (ret <= 0) continue; | if (ret <= 0) continue; | ||||
Show All 19 Lines |