2011-12-13 07:02:51 -08:00
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <stdint.h>
# include <sys/types.h>
# include <arpa/inet.h>
2011-12-16 07:56:36 -08:00
# include <time.h>
# include <ctype.h>
2011-12-13 07:02:51 -08:00
2011-12-20 05:29:21 -08:00
# include "dns.h"
2011-12-16 07:56:36 -08:00
2011-12-20 05:29:21 -08:00
# define BUFLEN 512
2011-12-19 20:20:50 -08:00
2011-12-13 07:02:51 -08:00
typedef enum {
CLASS_IN = 1 ,
QCLASS_ANY = 255
} dns_class ;
typedef enum {
TYPE_A = 1 ,
TYPE_NS = 2 ,
TYPE_CNAME = 5 ,
TYPE_SOA = 6 ,
TYPE_MX = 15 ,
TYPE_AAAA = 28 ,
TYPE_SRV = 33 ,
QTYPE_ANY = 255
} dns_type ;
// 0: ok
2011-12-16 07:56:36 -08:00
// -1: premature end of input, forward reference, component > 63 char, invalid character
2011-12-13 10:35:10 -08:00
// -2: insufficient space in output
2011-12-16 07:56:36 -08:00
int static parse_name ( const unsigned char * * inpos , const unsigned char * inend , const unsigned char * inbuf , char * buf , size_t bufsize ) {
2011-12-13 07:02:51 -08:00
size_t bufused = 0 ;
2011-12-13 10:35:10 -08:00
int init = 1 ;
2011-12-13 07:02:51 -08:00
do {
if ( * inpos = = inend )
return - 1 ;
2011-12-13 10:35:10 -08:00
// read length of next component
int octet = * ( ( * inpos ) + + ) ;
2011-12-13 07:02:51 -08:00
if ( octet = = 0 ) {
buf [ bufused ] = 0 ;
return 0 ;
}
2011-12-13 10:35:10 -08:00
// add dot in output
if ( ! init ) {
if ( bufused = = bufsize - 1 )
return - 2 ;
buf [ bufused + + ] = ' . ' ;
} else
init = 0 ;
// handle references
if ( ( octet & 0xC0 ) = = 0xC0 ) {
if ( * inpos = = inend )
return - 1 ;
int ref = ( ( octet - 0xC0 ) < < 8 ) + * ( ( * inpos ) + + ) ;
if ( ref < 0 | | ref > = ( * inpos ) - inbuf ) return - 1 ;
const unsigned char * newbuf = inbuf + ref ;
return parse_name ( & newbuf , * inpos , inbuf , buf + bufused , bufsize - bufused ) ;
}
if ( octet > 63 ) return - 1 ;
2011-12-16 07:56:36 -08:00
// copy label
2011-12-13 07:02:51 -08:00
while ( octet ) {
if ( * inpos = = inend )
return - 1 ;
if ( bufused = = bufsize - 1 )
return - 2 ;
2011-12-16 07:56:36 -08:00
int c = * ( ( * inpos ) + + ) ;
if ( c = = ' . ' )
return - 1 ;
2011-12-13 07:02:51 -08:00
octet - - ;
2011-12-16 07:56:36 -08:00
buf [ bufused + + ] = c ;
2011-12-13 07:02:51 -08:00
}
} while ( 1 ) ;
}
// 0: k
// -1: component > 63 characters
// -2: insufficent space in output
// -3: two subsequent dots
2011-12-20 05:29:21 -08:00
int static write_name ( unsigned char * * outpos , const unsigned char * outend , const char * name , int offset ) {
2011-12-13 07:02:51 -08:00
while ( * name ! = 0 ) {
char * dot = strchr ( name , ' . ' ) ;
2011-12-20 05:29:21 -08:00
const char * fin = dot ;
2011-12-13 07:02:51 -08:00
if ( ! dot ) fin = name + strlen ( name ) ;
if ( fin - name > 63 ) return - 1 ;
if ( fin = = name ) return - 3 ;
if ( outend - * outpos < fin - name + 2 ) return - 2 ;
2011-12-13 10:35:10 -08:00
* ( ( * outpos ) + + ) = fin - name ;
2011-12-13 07:02:51 -08:00
memcpy ( * outpos , name , fin - name ) ;
2011-12-13 10:35:10 -08:00
* outpos + = fin - name ;
2011-12-13 07:02:51 -08:00
if ( ! dot ) break ;
name = dot + 1 ;
}
2011-12-13 10:35:10 -08:00
if ( offset < 0 ) {
// no reference
if ( outend = = * outpos ) return - 2 ;
* ( ( * outpos ) + + ) = 0 ;
} else {
if ( outend - * outpos < 2 ) return - 2 ;
* ( ( * outpos ) + + ) = ( offset > > 8 ) | 0xC0 ;
* ( ( * outpos ) + + ) = offset & 0xFF ;
}
2011-12-13 07:02:51 -08:00
return 0 ;
}
2011-12-20 05:29:21 -08:00
int static write_record ( unsigned char * * outpos , const unsigned char * outend , const char * name , int offset , dns_type typ , dns_class cls , int ttl ) {
2011-12-13 07:02:51 -08:00
unsigned char * oldpos = * outpos ;
2011-12-13 10:35:10 -08:00
int error = 0 ;
2011-12-13 07:02:51 -08:00
// name
2011-12-13 10:35:10 -08:00
int ret = write_name ( outpos , outend , name , offset ) ;
if ( ret ) { error = ret ; goto error ; }
if ( outend - * outpos < 8 ) { error = - 4 ; goto error ; }
2011-12-13 07:02:51 -08:00
// type
2011-12-13 10:35:10 -08:00
* ( ( * outpos ) + + ) = typ > > 8 ; * ( ( * outpos ) + + ) = typ & 0xFF ;
2011-12-13 07:02:51 -08:00
// class
2011-12-13 10:35:10 -08:00
* ( ( * outpos ) + + ) = cls > > 8 ; * ( ( * outpos ) + + ) = cls & 0xFF ;
2011-12-13 07:02:51 -08:00
// ttl
2011-12-13 10:35:10 -08:00
* ( ( * outpos ) + + ) = ( ttl > > 24 ) & 0xFF ; * ( ( * outpos ) + + ) = ( ttl > > 16 ) & 0xFF ; * ( ( * outpos ) + + ) = ( ttl > > 8 ) & 0xFF ; * ( ( * outpos ) + + ) = ttl & 0xFF ;
return 0 ;
error :
* outpos = oldpos ;
return error ;
}
2011-12-20 05:29:21 -08:00
int static write_record_a ( unsigned char * * outpos , const unsigned char * outend , const char * name , int offset , dns_class cls , int ttl , const struct in_addr * ip ) {
2011-12-13 10:35:10 -08:00
unsigned char * oldpos = * outpos ;
int error = 0 ;
int ret = write_record ( outpos , outend , name , offset , TYPE_A , cls , ttl ) ;
if ( ret ) return ret ;
if ( outend - * outpos < 6 ) { error = - 5 ; goto error ; }
2011-12-13 07:02:51 -08:00
// rdlength
2011-12-13 10:35:10 -08:00
* ( ( * outpos ) + + ) = 0 ; * ( ( * outpos ) + + ) = 4 ;
2011-12-13 07:02:51 -08:00
// rdata
2011-12-16 07:56:36 -08:00
const unsigned char * pd = ( const unsigned char * ) ip ;
for ( int i = 0 ; i < 4 ; i + + )
* ( ( * outpos ) + + ) = pd [ i ] ;
2011-12-13 10:35:10 -08:00
return 0 ;
error :
* outpos = oldpos ;
return error ;
}
2011-12-20 05:29:21 -08:00
int static write_record_aaaa ( unsigned char * * outpos , const unsigned char * outend , const char * name , int offset , dns_class cls , int ttl , const struct in6_addr * ip ) {
2011-12-16 07:56:36 -08:00
unsigned char * oldpos = * outpos ;
int error = 0 ;
int ret = write_record ( outpos , outend , name , offset , TYPE_AAAA , cls , ttl ) ;
if ( ret ) return ret ;
if ( outend - * outpos < 6 ) { error = - 5 ; goto error ; }
// rdlength
* ( ( * outpos ) + + ) = 0 ; * ( ( * outpos ) + + ) = 16 ;
// rdata
const unsigned char * pd = ( const unsigned char * ) ip ;
for ( int i = 0 ; i < 16 ; i + + )
* ( ( * outpos ) + + ) = pd [ i ] ;
return 0 ;
error :
* outpos = oldpos ;
return error ;
}
2011-12-20 05:29:21 -08:00
int static write_record_ns ( unsigned char * * outpos , const unsigned char * outend , char * name , int offset , dns_class cls , int ttl , const char * ns ) {
2011-12-13 10:35:10 -08:00
unsigned char * oldpos = * outpos ;
int ret = write_record ( outpos , outend , name , offset , TYPE_NS , cls , ttl ) ;
if ( ret ) return ret ;
int error = 0 ;
if ( outend - * outpos < 2 ) { error = - 5 ; goto error ; }
( * outpos ) + = 2 ;
unsigned char * curpos = * outpos ;
ret = write_name ( outpos , outend , ns , - 1 ) ;
if ( ret ) { error = ret ; goto error ; }
curpos [ - 2 ] = ( * outpos - curpos ) > > 8 ;
curpos [ - 1 ] = ( * outpos - curpos ) & 0xFF ;
2011-12-13 07:02:51 -08:00
return 0 ;
error :
* outpos = oldpos ;
2011-12-13 10:35:10 -08:00
return error ;
2011-12-13 07:02:51 -08:00
}
2011-12-20 05:29:21 -08:00
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 ,
2011-12-16 07:56:36 -08:00
uint32_t serial , uint32_t refresh , uint32_t retry , uint32_t expire , uint32_t minimum ) {
unsigned char * oldpos = * outpos ;
int ret = write_record ( outpos , outend , name , offset , TYPE_SOA , cls , ttl ) ;
if ( ret ) return ret ;
int error = 0 ;
if ( outend - * outpos < 2 ) { error = - 5 ; goto error ; }
( * outpos ) + = 2 ;
unsigned char * curpos = * outpos ;
ret = write_name ( outpos , outend , mname , - 1 ) ;
if ( ret ) { error = ret ; goto error ; }
ret = write_name ( outpos , outend , rname , - 1 ) ;
if ( ret ) { error = ret ; goto error ; }
if ( outend - * outpos < 20 ) { error = - 5 ; goto error ; }
* ( ( * outpos ) + + ) = ( serial > > 24 ) & 0xFF ; * ( ( * outpos ) + + ) = ( serial > > 16 ) & 0xFF ; * ( ( * outpos ) + + ) = ( serial > > 8 ) & 0xFF ; * ( ( * outpos ) + + ) = serial & 0xFF ;
* ( ( * outpos ) + + ) = ( refresh > > 24 ) & 0xFF ; * ( ( * outpos ) + + ) = ( refresh > > 16 ) & 0xFF ; * ( ( * outpos ) + + ) = ( refresh > > 8 ) & 0xFF ; * ( ( * outpos ) + + ) = refresh & 0xFF ;
* ( ( * outpos ) + + ) = ( retry > > 24 ) & 0xFF ; * ( ( * outpos ) + + ) = ( retry > > 16 ) & 0xFF ; * ( ( * outpos ) + + ) = ( retry > > 8 ) & 0xFF ; * ( ( * outpos ) + + ) = retry & 0xFF ;
* ( ( * outpos ) + + ) = ( expire > > 24 ) & 0xFF ; * ( ( * outpos ) + + ) = ( expire > > 16 ) & 0xFF ; * ( ( * outpos ) + + ) = ( expire > > 8 ) & 0xFF ; * ( ( * outpos ) + + ) = expire & 0xFF ;
* ( ( * outpos ) + + ) = ( minimum > > 24 ) & 0xFF ; * ( ( * outpos ) + + ) = ( minimum > > 16 ) & 0xFF ; * ( ( * outpos ) + + ) = ( minimum > > 8 ) & 0xFF ; * ( ( * outpos ) + + ) = minimum & 0xFF ;
curpos [ - 2 ] = ( * outpos - curpos ) > > 8 ;
curpos [ - 1 ] = ( * outpos - curpos ) & 0xFF ;
return 0 ;
error :
* outpos = oldpos ;
return error ;
}
2011-12-20 05:29:21 -08:00
ssize_t static dnshandle ( dns_opt_t * opt , const unsigned char * inbuf , size_t insize , unsigned char * outbuf ) {
2011-12-13 07:02:51 -08:00
int error = 0 ;
if ( insize < 12 ) // DNS header
return - 1 ;
// copy id
outbuf [ 0 ] = inbuf [ 0 ] ;
outbuf [ 1 ] = inbuf [ 1 ] ;
// copy flags;
outbuf [ 2 ] = inbuf [ 2 ] ;
outbuf [ 3 ] = inbuf [ 3 ] ;
// clear error
2011-12-13 10:35:10 -08:00
outbuf [ 3 ] & = ~ 15 ;
2011-12-13 07:02:51 -08:00
// check qr
2011-12-16 07:56:36 -08:00
if ( inbuf [ 2 ] & 128 ) { /* printf("Got response?\n"); */ error = 1 ; goto error ; }
2011-12-13 07:02:51 -08:00
// check opcode
2011-12-16 07:56:36 -08:00
if ( ( ( inbuf [ 2 ] & 120 ) > > 3 ) ! = 0 ) { /* printf("Opcode nonzero?\n"); */ error = 4 ; goto error ; }
2011-12-13 07:02:51 -08:00
// check Z
2011-12-16 07:56:36 -08:00
if ( ( ( inbuf [ 3 ] & 112 ) > > 4 ) ! = 0 ) { /* printf("Z nonzero?\n"); */ error = 1 ; goto error ; }
2011-12-13 07:02:51 -08:00
// unset TC
2011-12-13 10:35:10 -08:00
outbuf [ 2 ] & = ~ 2 ;
2011-12-13 07:02:51 -08:00
// unset RA
2011-12-13 10:35:10 -08:00
outbuf [ 3 ] & = ~ 128 ;
2011-12-13 07:02:51 -08:00
// check questions
2011-12-13 10:35:10 -08:00
int nquestion = ( inbuf [ 4 ] < < 8 ) + inbuf [ 5 ] ;
2011-12-16 07:56:36 -08:00
if ( nquestion = = 0 ) { /* printf("No questions?\n"); */ error = 0 ; goto error ; }
if ( nquestion > 1 ) { /* printf("Multiple questions %i?\n", nquestion); */ error = 4 ; goto error ; }
2011-12-13 07:02:51 -08:00
const unsigned char * inpos = inbuf + 12 ;
const unsigned char * inend = inbuf + insize ;
char name [ 256 ] ;
2011-12-13 10:35:10 -08:00
int offset = inpos - inbuf ;
int ret = parse_name ( & inpos , inend , inbuf , name , 256 ) ;
2011-12-13 07:02:51 -08:00
if ( ret = = - 1 ) { error = 1 ; goto error ; }
if ( ret = = - 2 ) { error = 5 ; goto error ; }
2011-12-20 05:29:21 -08:00
int namel = strlen ( name ) , hostl = strlen ( opt - > host ) ;
if ( strcmp ( name , opt - > host ) & & ( namel < hostl + 2 | | name [ namel - hostl - 1 ] ! = ' . ' | | strcmp ( name + namel - hostl , opt - > host ) ) ) { error = 5 ; goto error ; }
2011-12-13 07:02:51 -08:00
if ( inend - inpos < 4 ) { error = 1 ; goto error ; }
// copy question to output
memcpy ( outbuf + 12 , inbuf + 12 , inpos + 4 - ( inbuf + 12 ) ) ;
// set counts
outbuf [ 4 ] = 0 ; outbuf [ 5 ] = 1 ;
outbuf [ 6 ] = 0 ; outbuf [ 7 ] = 0 ;
outbuf [ 8 ] = 0 ; outbuf [ 9 ] = 0 ;
outbuf [ 10 ] = 0 ; outbuf [ 11 ] = 0 ;
2011-12-13 10:35:10 -08:00
// set qr
outbuf [ 2 ] | = 128 ;
2011-12-13 07:02:51 -08:00
2011-12-13 10:35:10 -08:00
int typ = ( inpos [ 0 ] < < 8 ) + inpos [ 1 ] ;
int cls = ( inpos [ 2 ] < < 8 ) + inpos [ 3 ] ;
2011-12-13 07:02:51 -08:00
inpos + = 4 ;
unsigned char * outpos = outbuf + ( inpos - inbuf ) ;
unsigned char * outend = outbuf + BUFLEN ;
2011-12-20 05:29:21 -08:00
printf ( " DNS: Request host='%s' type=%i class=%i \n " , name , typ , cls ) ;
2011-12-13 10:35:10 -08:00
// calculate size of authority section
int auth_size = 0 ;
if ( ! ( ( typ = = TYPE_NS | | typ = = QTYPE_ANY ) & & ( cls = = CLASS_IN | | cls = = QCLASS_ANY ) ) ) {
// authority section will be necessary
unsigned char * oldpos = outpos ;
2011-12-20 05:29:21 -08:00
write_record_ns ( & oldpos , outend , " " , offset , CLASS_IN , 0 , opt - > ns ) ;
2011-12-13 10:35:10 -08:00
auth_size = oldpos - outpos ;
2011-12-16 07:56:36 -08:00
// printf("Authority section will claim %i bytes\n", auth_size);
2011-12-13 10:35:10 -08:00
}
// Answer section
int have_ns = 0 ;
2011-12-16 07:56:36 -08:00
// NS records
2011-12-13 10:35:10 -08:00
if ( ( typ = = TYPE_NS | | typ = = QTYPE_ANY ) & & ( cls = = CLASS_IN | | cls = = QCLASS_ANY ) ) {
2011-12-20 05:29:21 -08:00
int ret2 = write_record_ns ( & outpos , outend - auth_size , " " , offset , CLASS_IN , opt - > nsttl , opt - > ns ) ;
2011-12-16 07:56:36 -08:00
// printf("wrote NS record: %i\n", ret2);
2011-12-13 10:35:10 -08:00
if ( ! ret2 ) { outbuf [ 7 ] + + ; have_ns + + ; }
}
2011-12-16 07:56:36 -08:00
// SOA records
if ( ( typ = = TYPE_SOA | | typ = = QTYPE_ANY ) & & ( cls = = CLASS_IN | | cls = = QCLASS_ANY ) ) {
2011-12-20 05:29:21 -08:00
int ret2 = write_record_soa ( & outpos , outend - auth_size , " " , offset , CLASS_IN , opt - > nsttl , opt - > ns , opt - > mbox , time ( NULL ) , 604800 , 86400 , 2592000 , 604800 ) ;
2011-12-16 07:56:36 -08:00
// printf("wrote SOA record: %i\n", ret2);
if ( ! ret2 ) { outbuf [ 7 ] + + ; }
}
2011-12-13 10:35:10 -08:00
2011-12-16 07:56:36 -08:00
// A records
2011-12-13 10:35:10 -08:00
if ( ( typ = = TYPE_A | | typ = = QTYPE_ANY ) & & ( cls = = CLASS_IN | | cls = = QCLASS_ANY ) ) {
2011-12-20 13:18:13 -08:00
struct in_addr addr [ 32 ] ;
int naddr = opt - > cb ( addr , 32 , 1 ) ;
2011-12-19 20:20:50 -08:00
int n = 0 ;
while ( n < naddr ) {
2011-12-20 05:29:21 -08:00
int ret = write_record_a ( & outpos , outend - auth_size , " " , offset , CLASS_IN , opt - > datattl , & addr [ n ] ) ;
2011-12-16 07:56:36 -08:00
// printf("wrote A record: %i\n", ret);
2011-12-13 10:35:10 -08:00
if ( ! ret ) {
2011-12-19 20:20:50 -08:00
n + + ;
2011-12-13 10:35:10 -08:00
outbuf [ 7 ] + + ;
} else
break ;
2011-12-19 20:20:50 -08:00
}
2011-12-13 10:35:10 -08:00
}
// Authority section
if ( ! have_ns ) {
2011-12-20 05:29:21 -08:00
int ret2 = write_record_ns ( & outpos , outend , " " , offset , CLASS_IN , opt - > nsttl , opt - > ns ) ;
2011-12-16 07:56:36 -08:00
// printf("wrote NS record: %i\n", ret2);
2011-12-13 10:35:10 -08:00
if ( ! ret2 ) {
outbuf [ 9 ] + + ;
}
2011-12-13 07:02:51 -08:00
}
// set AA
2011-12-13 10:35:10 -08:00
outbuf [ 2 ] | = 4 ;
2011-12-13 07:02:51 -08:00
return outpos - outbuf ;
error :
// set error
2011-12-13 10:35:10 -08:00
outbuf [ 3 ] | = error & 0xF ;
2011-12-13 07:02:51 -08:00
// set counts
outbuf [ 4 ] = 0 ; outbuf [ 5 ] = 0 ;
outbuf [ 6 ] = 0 ; outbuf [ 7 ] = 0 ;
outbuf [ 8 ] = 0 ; outbuf [ 9 ] = 0 ;
outbuf [ 10 ] = 0 ; outbuf [ 11 ] = 0 ;
return 12 ;
}
2011-12-20 05:29:21 -08:00
int dnsserver ( dns_opt_t * opt ) {
2011-12-13 07:02:51 -08:00
struct sockaddr_in si_me , si_other ;
2011-12-16 07:56:36 -08:00
socklen_t s , slen = sizeof ( si_other ) ;
2011-12-13 07:02:51 -08:00
unsigned char inbuf [ BUFLEN ] , outbuf [ BUFLEN ] ;
if ( ( s = socket ( AF_INET , SOCK_DGRAM , IPPROTO_UDP ) ) = = - 1 )
return - 1 ;
memset ( ( char * ) & si_me , 0 , sizeof ( si_me ) ) ;
si_me . sin_family = AF_INET ;
2011-12-20 05:29:21 -08:00
si_me . sin_port = htons ( opt - > port ) ;
2011-12-13 07:02:51 -08:00
si_me . sin_addr . s_addr = INADDR_ANY ;
2011-12-13 10:35:10 -08:00
if ( bind ( s , ( struct sockaddr * ) & si_me , sizeof ( si_me ) ) = = - 1 )
2011-12-13 07:02:51 -08:00
return - 2 ;
do {
2011-12-13 10:35:10 -08:00
ssize_t insize = recvfrom ( s , inbuf , BUFLEN , 0 , ( struct sockaddr * ) & si_other , & slen ) ;
2011-12-16 07:56:36 -08:00
unsigned char * addr = ( unsigned char * ) & si_other . sin_addr . s_addr ;
2011-12-25 16:04:24 -08:00
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 ) ;
2011-12-13 07:02:51 -08:00
if ( insize > 0 ) {
2011-12-20 05:29:21 -08:00
ssize_t ret = dnshandle ( opt , inbuf , insize , outbuf ) ;
2011-12-13 07:02:51 -08:00
if ( ret > 0 )
2011-12-13 10:35:10 -08:00
sendto ( s , outbuf , ret , 0 , ( struct sockaddr * ) & si_other , slen ) ;
2011-12-13 07:02:51 -08:00
}
} while ( 1 ) ;
return 0 ;
}