Changeset View
Changeset View
Standalone View
Standalone View
src/seeder/dns.cpp
Show All 31 Lines | |||||
#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; | ||||
uint8_t 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; | ||||
int parse_name(const uint8_t **inpos, const uint8_t *inend, | int parse_name(const uint8_t **inpos, const uint8_t *inend, | ||||
const uint8_t *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 uint8_t *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) { | ||||
if (bufused == bufsize - 1) return -2; | return -1; | ||||
} | |||||
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 | ||||
static int write_name(uint8_t **outpos, const uint8_t *outend, const char *name, | static int write_name(uint8_t **outpos, const uint8_t *outend, const char *name, | ||||
int offset) { | int offset) { | ||||
while (*name != 0) { | while (*name != 0) { | ||||
const char *dot = strchr(name, '.'); | const char *dot = strchr(name, '.'); | ||||
const char *fin = dot; | const char *fin = dot; | ||||
if (!dot) fin = name + strlen(name); | if (!dot) { | ||||
if (fin - name > 63) return -1; | fin = name + strlen(name); | ||||
if (fin == name) return -3; | } | ||||
if (outend - *outpos < fin - name + 2) return -2; | if (fin - name > 63) { | ||||
return -1; | |||||
} | |||||
if (fin == name) { | |||||
return -3; | |||||
} | |||||
if (outend - *outpos < fin - name + 2) { | |||||
return -2; | |||||
} | |||||
*((*outpos)++) = fin - name; | *((*outpos)++) = fin - name; | ||||
memcpy(*outpos, name, fin - name); | memcpy(*outpos, name, fin - name); | ||||
*outpos += fin - name; | *outpos += fin - name; | ||||
if (!dot) break; | if (!dot) { | ||||
break; | |||||
} | |||||
name = dot + 1; | name = dot + 1; | ||||
} | } | ||||
if (offset < 0) { | if (offset < 0) { | ||||
// no reference | // no reference | ||||
if (outend == *outpos) return -2; | if (outend == *outpos) { | ||||
return -2; | |||||
} | |||||
*((*outpos)++) = 0; | *((*outpos)++) = 0; | ||||
} 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; | ||||
} | } | ||||
static int write_record(uint8_t **outpos, const uint8_t *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, | ||||
Show All 25 Lines | |||||
error: | error: | ||||
*outpos = oldpos; | *outpos = oldpos; | ||||
return error; | return error; | ||||
} | } | ||||
static int write_record_a(uint8_t **outpos, const uint8_t *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; | |||||
} | |||||
uint8_t *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; | ||||
} | } | ||||
static int write_record_aaaa(uint8_t **outpos, const uint8_t *outend, | static int write_record_aaaa(uint8_t **outpos, const uint8_t *outend, | ||||
const char *name, int offset, dns_class cls, | const char *name, int offset, dns_class cls, | ||||
int ttl, const addr_t *ip) { | int ttl, const addr_t *ip) { | ||||
if (ip->v != 6) return -6; | if (ip->v != 6) { | ||||
return -6; | |||||
} | |||||
uint8_t *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; | ||||
} | } | ||||
static int write_record_ns(uint8_t **outpos, const uint8_t *outend, | static int write_record_ns(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, | ||||
▲ Show 20 Lines • Show All 212 Lines • ▼ Show 20 Lines | if (nquestion > 1) { | ||||
uint8_t *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, | opt->ns, opt->mbox, time(NULL), 604800, 86400, | ||||
2592000, 604800); | 2592000, 604800); | ||||
if (max_auth_size < newpos - outpos) | if (max_auth_size < newpos - outpos) { | ||||
max_auth_size = newpos - outpos; | max_auth_size = newpos - outpos; | ||||
} | |||||
// fprintf(stdout, "Authority section will claim %i bytes max\n", | // fprintf(stdout, "Authority section will claim %i bytes max\n", | ||||
// max_auth_size); | // max_auth_size); | ||||
} | } | ||||
// Answer section | // Answer section | ||||
// NS records | // NS records | ||||
if ((typ == TYPE_NS || typ == QTYPE_ANY) && | if ((typ == TYPE_NS || typ == QTYPE_ANY) && | ||||
▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static int listenSocket = -1; | static int listenSocket = -1; | ||||
int dnsserver(dns_opt_t *opt) { | int dnsserver(dns_opt_t *opt) { | ||||
struct sockaddr_in6 si_other; | struct sockaddr_in6 si_other; | ||||
int senderSocket = -1; | int senderSocket = -1; | ||||
senderSocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); | senderSocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); | ||||
if (senderSocket == -1) return -3; | if (senderSocket == -1) { | ||||
return -3; | |||||
} | |||||
int replySocket; | int replySocket; | ||||
if (listenSocket == -1) { | if (listenSocket == -1) { | ||||
struct sockaddr_in6 si_me; | struct sockaddr_in6 si_me; | ||||
if ((listenSocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1) { | if ((listenSocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1) { | ||||
listenSocket = -1; | listenSocket = -1; | ||||
return -1; | return -1; | ||||
} | } | ||||
replySocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); | replySocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); | ||||
if (replySocket == -1) { | if (replySocket == -1) { | ||||
close(listenSocket); | close(listenSocket); | ||||
return -1; | return -1; | ||||
} | } | ||||
int sockopt = 1; | int sockopt = 1; | ||||
setsockopt(listenSocket, IPPROTO_IPV6, DSTADDR_SOCKOPT, &sockopt, | setsockopt(listenSocket, IPPROTO_IPV6, DSTADDR_SOCKOPT, &sockopt, | ||||
sizeof sockopt); | sizeof sockopt); | ||||
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; | ||||
} | } | ||||
} | |||||
uint8_t inbuf[BUFLEN], outbuf[BUFLEN]; | uint8_t inbuf[BUFLEN], outbuf[BUFLEN]; | ||||
struct iovec iov[1] = { | struct iovec iov[1] = { | ||||
{ | { | ||||
.iov_base = inbuf, | .iov_base = inbuf, | ||||
.iov_len = sizeof(inbuf), | .iov_len = sizeof(inbuf), | ||||
}, | }, | ||||
}; | }; | ||||
union control_data cmsg; | union control_data cmsg; | ||||
msghdr msg; | msghdr msg; | ||||
msg.msg_name = &si_other; | msg.msg_name = &si_other; | ||||
msg.msg_namelen = sizeof(si_other); | msg.msg_namelen = sizeof(si_other); | ||||
msg.msg_iov = iov; | msg.msg_iov = iov; | ||||
msg.msg_iovlen = 1; | msg.msg_iovlen = 1; | ||||
msg.msg_control = &cmsg; | msg.msg_control = &cmsg; | ||||
msg.msg_controllen = sizeof(cmsg); | msg.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); | ||||
// uint8_t *addr = (uint8_t*)&si_other.sin_addr.s_addr; | // uint8_t *addr = (uint8_t*)&si_other.sin_addr.s_addr; | ||||
// fprintf(stdout, "DNS: Request %llu from %i.%i.%i.%i:%i of %i | // fprintf(stdout, "DNS: Request %llu from %i.%i.%i.%i:%i of %i | ||||
// bytes\n", (unsigned long long)(opt->nRequests), addr[0], addr[1], | // bytes\n", (unsigned long long)(opt->nRequests), addr[0], addr[1], | ||||
// addr[2], addr[3], ntohs(si_other.sin_port), (int)insize); | // addr[2], 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; | |||||
} | |||||
bool handled = false; | bool handled = false; | ||||
for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; | for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; | ||||
hdr = CMSG_NXTHDR(&msg, hdr)) { | hdr = CMSG_NXTHDR(&msg, hdr)) { | ||||
if (hdr->cmsg_level == IPPROTO_IP && | if (hdr->cmsg_level == IPPROTO_IP && | ||||
hdr->cmsg_type == DSTADDR_SOCKOPT) { | hdr->cmsg_type == DSTADDR_SOCKOPT) { | ||||
msg.msg_iov[0].iov_base = outbuf; | msg.msg_iov[0].iov_base = outbuf; | ||||
msg.msg_iov[0].iov_len = ret; | msg.msg_iov[0].iov_len = ret; | ||||
sendmsg(listenSocket, &msg, 0); | sendmsg(listenSocket, &msg, 0); | ||||
msg.msg_iov[0].iov_base = inbuf; | msg.msg_iov[0].iov_base = inbuf; | ||||
msg.msg_iov[0].iov_len = sizeof(inbuf); | msg.msg_iov[0].iov_len = sizeof(inbuf); | ||||
handled = true; | handled = true; | ||||
} | } | ||||
} | } | ||||
if (!handled) | if (!handled) { | ||||
sendto(listenSocket, outbuf, ret, 0, (struct sockaddr *)&si_other, | sendto(listenSocket, outbuf, ret, 0, (struct sockaddr *)&si_other, | ||||
sizeof(si_other)); | sizeof(si_other)); | ||||
} | } | ||||
} | |||||
return 0; | return 0; | ||||
} | } |