From 7b62739c13790a7a7ff01bd5637374b7597c3fe5 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Sat, 16 Jun 2018 02:36:04 +0800 Subject: [PATCH] Add systemd script --- etc/default/smartdns | 5 + etc/init.d/smartdns | 77 ++ smartdns.conf => etc/smartdns/smartdns.conf | 6 +- install | 204 ++++++ Makefile => src/Makefile | 2 +- conf.c => src/conf.c | 8 + conf.h => src/conf.h | 0 dns.c => src/dns.c | 194 ++++- dns.h => src/dns.h | 32 +- dns_client.c => src/dns_client.c | 160 +++-- dns_client.h => src/dns_client.h | 6 +- dns_server.c => src/dns_server.c | 20 +- dns_server.h => src/dns_server.h | 0 fast_ping.c => src/fast_ping.c | 6 +- fast_ping.h => src/fast_ping.h | 6 +- {include => src/include}/atomic.h | 0 {include => src/include}/bitmap.h | 0 {include => src/include}/bitops.h | 0 {include => src/include}/cache.h | 0 {include => src/include}/findbit.h | 0 {include => src/include}/gcc_builtin.h | 0 {include => src/include}/hash.h | 0 {include => src/include}/hashtable.h | 0 {include => src/include}/jhash.h | 0 {include => src/include}/list.h | 0 {lib => src/lib}/bitops.c | 0 {lib => src/lib}/cache.c | 0 smartdns.c => src/smartdns.c | 116 ++- tlog.c => src/tlog.c | 0 tlog.h => src/tlog.h | 0 util.c => src/util.c | 2 +- util.h => src/util.h | 2 +- systemd/smartdns.service | 14 + test-dns.c | 755 -------------------- 34 files changed, 746 insertions(+), 869 deletions(-) create mode 100644 etc/default/smartdns create mode 100644 etc/init.d/smartdns rename smartdns.conf => etc/smartdns/smartdns.conf (72%) create mode 100644 install rename Makefile => src/Makefile (76%) mode change 100755 => 100644 rename conf.c => src/conf.c (87%) rename conf.h => src/conf.h (100%) rename dns.c => src/dns.c (80%) rename dns.h => src/dns.h (88%) rename dns_client.c => src/dns_client.c (90%) rename dns_client.h => src/dns_client.h (87%) rename dns_server.c => src/dns_server.c (98%) rename dns_server.h => src/dns_server.h (100%) rename fast_ping.c => src/fast_ping.c (95%) mode change 100755 => 100644 rename fast_ping.h => src/fast_ping.h (86%) mode change 100755 => 100644 rename {include => src/include}/atomic.h (100%) mode change 100755 => 100644 rename {include => src/include}/bitmap.h (100%) rename {include => src/include}/bitops.h (100%) rename {include => src/include}/cache.h (100%) rename {include => src/include}/findbit.h (100%) rename {include => src/include}/gcc_builtin.h (100%) rename {include => src/include}/hash.h (100%) rename {include => src/include}/hashtable.h (100%) rename {include => src/include}/jhash.h (100%) rename {include => src/include}/list.h (100%) rename {lib => src/lib}/bitops.c (100%) rename {lib => src/lib}/cache.c (100%) rename smartdns.c => src/smartdns.c (59%) mode change 100755 => 100644 rename tlog.c => src/tlog.c (100%) rename tlog.h => src/tlog.h (100%) rename util.c => src/util.c (98%) rename util.h => src/util.h (88%) create mode 100644 systemd/smartdns.service delete mode 100755 test-dns.c diff --git a/etc/default/smartdns b/etc/default/smartdns new file mode 100644 index 0000000..f7ec525 --- /dev/null +++ b/etc/default/smartdns @@ -0,0 +1,5 @@ +# Default settings for smartdns server. This file is sourced by /bin/sh from +# /etc/init.d/smartdns. + +# Options to pass to smartdns +SMART_DNS_OPTS= diff --git a/etc/init.d/smartdns b/etc/init.d/smartdns new file mode 100644 index 0000000..359596f --- /dev/null +++ b/etc/init.d/smartdns @@ -0,0 +1,77 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: smartdns +# Required-Start: $network +# Required-Stop: $network +# Default-Start: 2 3 4 5 +# Default-Stop: +# Short-Description: Start smartdns server +### END INIT INFO + +PATH=/sbin:/bin:/usr/sbin:/usr/bin + +SMARTDNS=/usr/sbin/smartdns +PIDFILE=/var/run/smartdns.pid + +test -x $SMARTDNS || exit 5 + +case $1 in + start) + $SMARTDNS $JAIL_SHELL_OPTS + while true; do + if [ -e "$PIDFILE" ]; then + break; + fi + sleep .5 + done + PID="`cat $PIDFILE 2>/dev/null`" + if [ -z "$PID" ]; then + echo "start smartdns server failed." + exit 1 + fi + if [ ! -e "/proc/$PID" ]; then + echo "start smartdns server failed." + exit 1 + fi + echo "start smartdns server success." + ;; + stop) + if [ ! -f "$PIDFILE" ]; then + echo "smartdns server is stopped." + exit 0 + fi + PID="`cat $PIDFILE 2>/dev/null`" + if [ ! -e "/proc/$PID" ] || [ -z "$PID" ]; then + echo "smartdns server is stopped" + exit 0 + fi + + kill -TERM $PID + if [ $? -ne 0 ]; then + echo "Stop smartdns server failed." + exit 1; + fi + rm -f $PIDFILE + echo "Stop smartdns server success." + ;; + restart) + $0 stop && sleep 1 && $0 start + ;; + status) + PID="`cat $PIDFILE 2>/dev/null`" + if [ ! -e "/proc/$PID" ] || [ -z "$PID" ]; then + echo "smartdns server is not running." + exit 1 + fi + echo "smartdns server is running." + status=$? + ;; + *) + echo "Usage: $0 {start|stop|restart|status}" + exit 2 + ;; +esac + +exit $status + diff --git a/smartdns.conf b/etc/smartdns/smartdns.conf similarity index 72% rename from smartdns.conf rename to etc/smartdns/smartdns.conf index 1bfc950..6cd8436 100644 --- a/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -5,13 +5,15 @@ bind [::]:53 cache-size 1024 loglevel error -#server 192.168.1.1 +#server 8.8.8.8 server 114.114.114.114 server 123.207.137.88 server 119.29.29.29 server 223.5.5.5 +#BAU DNS +server 223.113.97.99 server 208.67.222.222:5353 server 202.141.178.13:5353 #server 77.88.8.8:53 server 202.141.162.123:53 -#server 101.132.183.99:53 \ No newline at end of file +#server 101.132.183.99:53 diff --git a/install b/install new file mode 100644 index 0000000..ad34b60 --- /dev/null +++ b/install @@ -0,0 +1,204 @@ +#!/bin/sh +# +# Copyright (C) 2018 Ruilin Peng (Nick) +# + +INST_DIR=$(cd $(dirname $0);pwd) + +showhelp() +{ + echo "Usage: install [OPTION]" + echo "Options:" + echo " -i install smartdns." + echo " -u uninstall smartdns." + echo " --prefix [dir] prefix directory." + echo " -h show this message." +} + +start_service() +{ + if [ $ISSYSTEMD -ne 0 ]; then + chkconfig smartdns on + service smartdns start + return $? + fi + + systemctl daemon-reload + systemctl enable smartdns + systemctl start smartdns +} + +stop_service() +{ + if [ $ISSYSTEMD -ne 0 ]; then + service smartdns stop + chkconfig smartdns off + return 0 + fi + + systemctl stop smartdns + systemctl disable smartdns + + return 0 +} + +clean_service() +{ + if [ $ISSYSTEMD -ne 0 ]; then + return 0 + fi + systemctl daemon-reload +} + +get_systemd_path() +{ + service="`systemctl --no-legend| grep .service | head -n 1 | awk '{print $1}'`" + SERVICE_PATH="`systemctl show $service | grep FragmentPath | awk -F'=' '{print $2}'`" + dirname $SERVICE_PATH +} + +install_files() +{ + install -v -d $SMARTDNS_CONF_DIR + if [ $? -ne 0 ]; then + return 1 + fi + + install -v -m 0755 -t $PREFIX/usr/sbin src/smartdns + if [ $? -ne 0 ]; then + return 1 + fi + + install -v -m 0640 -t $PREFIX$SMARTDNS_CONF_DIR etc/smartdns/smartdns.conf + if [ $? -ne 0 ]; then + return 1 + fi + + install -v -m 0640 -t $PREFIX/etc/default etc/default/smartdns + if [ $? -ne 0 ]; then + return 1 + fi + + install -v -m 0755 -t $SMARTDNS_INIT_DIR etc/init.d/smartdns + if [ $? -ne 0 ]; then + return 1 + fi + + if [ $ISSYSTEMD -eq 0 ]; then + SYSTEM_UNIT_PATH="`get_systemd_path`" + if [ -z "$SYSTEM_UNIT_PATH" ]; then + return 1 + fi + install -v -m 0644 -t $PREFIX$SYSTEM_UNIT_PATH systemd/smartdns.service + if [ $? -ne 0 ]; then + return 1 + fi + fi + + return 0 +} + +uninstall_smartdns() +{ + if [ -z "$PREFIX" ]; then + stop_service + fi + rm -f $PREFIX$SMARTDNS_CONF_DIR/smartdns.conf + rmdir $PREFIX$SMARTDNS_CONF_DIR + rm -f $PREFIX/usr/sbin/smartdns + rm -f $PREFIX/etc/default/smartdns + + if [ $ISSYSTEMD -eq 0 ]; then + SYSTEM_UNIT_PATH="`get_systemd_path`" + if [ ! -z "$SYSTEM_UNIT_PATH" ]; then + rm -f $PREFIX$SYSTEM_UNIT_PATH/smartdns.service + fi + fi + + if [ -z "$PREFIX" ]; then + clean_service + fi +} + + +install_smartdns() +{ + local ret + + install_files + ret=$? + if [ $ret -ne 0 ]; then + uninstall_smartdns + return $ret + fi + + if [ -z "$PREFIX" ]; then + start_service + fi + + return 0 +} + + + +init_dir() +{ + SMARTDNS_CONF_DIR=$PREFIX/etc/smartdns + SMARTDNS_INIT_DIR=$PREFIX/etc/init.d + which systemctl >/dev/null 2>&1 + ISSYSTEMD="$?" + + cd $INST_DIR +} + +main() +{ + ACTION="" + + OPTS=`getopt -o iuh --long help,prefix: \ + -n "" -- "$@"` + + if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi + + # Note the quotes around `$TEMP': they are essential! + eval set -- "$OPTS" + + while true; do + case "$1" in + --prefix) + PREFIX="$2" + shift 2;; + -h | --help ) + showhelp + return 0 + shift ;; + -i ) + ACTION="INSTALL" + shift ;; + -u ) + ACTION="UNINSTALL" + shift ;; + -- ) shift; break ;; + * ) break ;; + esac + done + + init_dir + + if [ -z "$ACTION" ]; then + showhelp + return 0 + elif [ "$ACTION" = "INSTALL" ]; then + install_smartdns + return $? + elif [ "$ACTION" = "UNINSTALL" ]; then + uninstall_smartdns + return 0 + fi + +} + +main $@ +exit $? + + diff --git a/Makefile b/src/Makefile old mode 100755 new mode 100644 similarity index 76% rename from Makefile rename to src/Makefile index 072231f..9351ff9 --- a/Makefile +++ b/src/Makefile @@ -1,7 +1,7 @@ BIN=smartdns OBJS=smartdns.o fast_ping.o lib/bitops.o dns_client.o dns_server.o dns.o util.o tlog.o conf.o -CFLAGS=-g -O0 -Wall +CFLAGS=-g -O0 -Wall -Wstrict-prototypes -fno-omit-frame-pointer -Wstrict-aliasing CFLAGS +=-Iinclude CFLAGS += -DBASE_FILE_NAME=\"$(notdir $<)\" CXXFLAGS=-g -O0 -Wall -std=c++11 diff --git a/conf.c b/src/conf.c similarity index 87% rename from conf.c rename to src/conf.c index 1c207d6..a901ab6 100644 --- a/conf.c +++ b/src/conf.c @@ -19,6 +19,7 @@ int dns_conf_loglevel = TLOG_ERROR; int config_bind(char *value) { + /* server bind address */ strncpy(dns_conf_server_ip, value, DNS_MAX_IPLEN); return 0; @@ -36,10 +37,12 @@ int config_server(char *value, dns_conf_server_type_t type) } server = &dns_conf_servers[index]; + /* parse ip, port from value */ if (parse_ip(value, server->server, &port) != 0) { return -1; } + /* if port is not defined, set port to default 53 */ if (port == PORT_NOT_DEFINED) { port= DEFAULT_DNS_PORT; } @@ -68,6 +71,7 @@ int config_server_http(char *value) int config_cache_size(char *value) { + /* read dns cache size */ int cache_size = atoi(value); if (cache_size < 0) { return -1; @@ -80,6 +84,7 @@ int config_cache_size(char *value) int config_log_level(char *value) { + /* read log level and set */ if (strncmp("debug", value, MAX_LINE_LEN) == 0) { dns_conf_loglevel = TLOG_DEBUG; } else if (strncmp("info", value, MAX_LINE_LEN) == 0) { @@ -131,10 +136,12 @@ int load_conf(const char *file) continue; } + /* comment, skip */ if (key[0] == '#') { continue; } + /* if field format is not key = value, error */ if (filed_num != 2) { goto errout; } @@ -144,6 +151,7 @@ int load_conf(const char *file) continue; } + /* call item function */ if (config_item[i].item_func(value) != 0) { goto errout; } diff --git a/conf.h b/src/conf.h similarity index 100% rename from conf.h rename to src/conf.h diff --git a/dns.c b/src/dns.c similarity index 80% rename from dns.c rename to src/dns.c index 1a9f30f..c8cf929 100644 --- a/dns.c +++ b/src/dns.c @@ -18,8 +18,12 @@ #include "dns.h" #include "tlog.h" +#include #include #include +#include +#include +#include #define QR_MASK 0x8000 #define OPCODE_MASK 0x7800 @@ -28,25 +32,26 @@ #define RD_MASK 0x0100 #define RA_MASK 0x0080 #define RCODE_MASK 0x000F - #define DNS_RR_END (0XFFFF) +/* read short and move pointer */ short dns_read_short(unsigned char **buffer) { unsigned short value; value = ntohs(*((unsigned short *)(*buffer))); *buffer += 2; - return value; } +/* write char and move pointer */ void dns_write_char(unsigned char **buffer, unsigned char value) { **buffer = value; *buffer += 1; } +/* read char and move pointer */ unsigned char dns_read_char(unsigned char **buffer) { unsigned char value = **buffer; @@ -54,6 +59,7 @@ unsigned char dns_read_char(unsigned char **buffer) return value; } +/* write short and move pointer */ void dns_write_short(unsigned char **buffer, unsigned short value) { value = htons(value); @@ -61,6 +67,7 @@ void dns_write_short(unsigned char **buffer, unsigned short value) *buffer += 2; } +/* write int and move pointer */ void dns_write_int(unsigned char **buffer, unsigned int value) { value = htonl(value); @@ -68,6 +75,7 @@ void dns_write_int(unsigned char **buffer, unsigned int value) *buffer += 4; } +/* read int and move pointer */ unsigned int dns_read_int(unsigned char **buffer) { unsigned int value; @@ -78,11 +86,13 @@ unsigned int dns_read_int(unsigned char **buffer) return value; } +/* iterator get rrs begin */ struct dns_rrs *dns_get_rrs_start(struct dns_packet *packet, dns_rr_type type, int *count) { unsigned short start; struct dns_head *head = &packet->head; + /* get rrs count by rrs type */ switch (type) { case DNS_RRS_QD: *count = head->qdcount; @@ -105,13 +115,16 @@ struct dns_rrs *dns_get_rrs_start(struct dns_packet *packet, dns_rr_type type, i break; } + /* if not resource record, reutrn null */ if (start == DNS_RR_END) { return NULL; } + /* return rrs data start address */ return (struct dns_rrs *)(packet->data + start); } +/* iterator next rrs */ struct dns_rrs *dns_get_rrs_next(struct dns_packet *packet, struct dns_rrs *rrs) { if (rrs->next == DNS_RR_END) { @@ -121,28 +134,32 @@ struct dns_rrs *dns_get_rrs_next(struct dns_packet *packet, struct dns_rrs *rrs) return (struct dns_rrs *)(packet->data + rrs->next); } +/* iterator add rrs begin */ unsigned char *_dns_add_rrs_start(struct dns_packet *packet, int *maxlen) { struct dns_rrs *rrs; unsigned char *end = packet->data + packet->len; + rrs = (struct dns_rrs *)end; *maxlen = packet->size - packet->len - sizeof(*packet); if (packet->len >= packet->size - sizeof(*packet)) { + /* if size exceeds max packet size, return NULL */ return NULL; } return rrs->data; } +/* iterator add rrs end */ int dns_rr_add_end(struct dns_packet *packet, int type, dns_type_t rtype, int len) { struct dns_rrs *rrs; struct dns_rrs *rrs_next; struct dns_head *head = &packet->head; unsigned char *end = packet->data + packet->len; - rrs = (struct dns_rrs *)end; unsigned short *count; unsigned short *start; + rrs = (struct dns_rrs *)end; if (packet->len + len > packet->size - sizeof(*packet)) { return -1; } @@ -169,6 +186,7 @@ int dns_rr_add_end(struct dns_packet *packet, int type, dns_type_t rtype, int le break; } + /* add data to end of dns_packet, and set previouse rrs point to this rrs */ if (*start != DNS_RR_END) { rrs_next = (struct dns_rrs *)(packet->data + *start); while (rrs_next->next != DNS_RR_END) { @@ -179,21 +197,29 @@ int dns_rr_add_end(struct dns_packet *packet, int type, dns_type_t rtype, int le *start = packet->len; } - rrs->next = DNS_RR_END; //*start; - *count += 1; + /* update rrs head info */ rrs->len = len; rrs->type = rtype; + rrs->next = DNS_RR_END; + + /* update total data length */ + *count += 1; packet->len += len + sizeof(*rrs); return 0; } static inline int _dns_data_left_len(struct dns_data_context *data_context) { + /* check whether data length out of bound */ return data_context->maxsize - (data_context->ptr - data_context->data); } int _dns_add_qr_head(struct dns_data_context *data_context, char *domain, int qtype, int qclass) { + /* question head */ + /* |domain | + * |qtype | qclass | + */ while (1) { if (_dns_data_left_len(data_context) < 1) { return -1; @@ -223,7 +249,10 @@ int _dns_add_qr_head(struct dns_data_context *data_context, char *domain, int qt int _dns_get_qr_head(struct dns_data_context *data_context, char *domain, int maxsize, int *qtype, int *qclass) { int i; - + /* question head */ + /* |domain | + * |qtype | qclass | + */ for (i = 0; i < maxsize; i++) { if (_dns_data_left_len(data_context) < 1) { return -1; @@ -257,6 +286,12 @@ int _dns_add_rr_head(struct dns_data_context *data_context, char *domain, int qt { int len = 0; + /* resource record head */ + /* |domain | + * |qtype | qclass | + * | ttl | + * | rrlen | rrdata | + */ len = _dns_add_qr_head(data_context, domain, qtype, qclass); if (len < 0) { return -1; @@ -279,6 +314,12 @@ int _dns_get_rr_head(struct dns_data_context *data_context, char *domain, int ma { int len = 0; + /* resource record head */ + /* |domain | + * |qtype | qclass | + * | ttl | + * | rrlen | rrdata | + */ len = _dns_get_qr_head(data_context, domain, maxsize, qtype, qclass); if (_dns_data_left_len(data_context) < 6) { @@ -300,6 +341,12 @@ int dns_add_RAW(struct dns_packet *packet, dns_rr_type rrtype, dns_type_t rtype, int len = 0; struct dns_data_context data_context; + /* resource record */ + /* |domain | + * |qtype | qclass | + * | ttl | + * | rrlen | rrdata | + */ unsigned char *data = _dns_add_rrs_start(packet, &maxlen); if (data == NULL) { return -1; @@ -313,11 +360,13 @@ int dns_add_RAW(struct dns_packet *packet, dns_rr_type rrtype, dns_type_t rtype, data_context.ptr = data; data_context.maxsize = maxlen; + /* add rr head */ len = _dns_add_rr_head(&data_context, domain, rtype, DNS_C_IN, ttl, raw_len); if (len < 0) { return -1; } + /* add rr data */ memcpy(data_context.ptr, raw, raw_len); data_context.ptr += raw_len; len = data_context.ptr - data_context.data; @@ -333,12 +382,19 @@ int dns_get_RAW(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, void * int ret = 0; struct dns_data_context data_context; + /* resource record head */ + /* |domain | + * |qtype | qclass | + * | ttl | + * | rrlen | rrdata | + */ unsigned char *data = rrs->data; data_context.data = data; data_context.ptr = data; data_context.maxsize = rrs->len; + /* get rr head */ ret = _dns_get_rr_head(&data_context, domain, maxsize, &qtype, &qclass, ttl, &rr_len); if (ret < 0) { return -1; @@ -348,6 +404,7 @@ int dns_get_RAW(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, void * return -1; } + /* get rr data */ memcpy(raw, data_context.ptr, rr_len); data_context.ptr += rr_len; *raw_len = rr_len; @@ -415,6 +472,15 @@ int dns_get_AAAA(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, unsig int dns_add_SOA(struct dns_packet *packet, dns_rr_type type, char *domain, int ttl, struct dns_soa *soa) { + /* SOA */ + /*| mname | + *| rname | + *| serial | + *| refersh | + *| retry | + *| expire | + *| minimum | + */ unsigned char data[sizeof(*soa)]; unsigned char *ptr = data; int len = 0; @@ -443,6 +509,15 @@ int dns_get_SOA(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, struct unsigned char *ptr = data; int len = sizeof(data); + /* SOA */ + /*| mname | + *| rname | + *| serial | + *| refersh | + *| retry | + *| expire | + *| minimum | + */ if (dns_get_RAW(rrs, domain, maxsize, ttl, data, &len) != 0) { return -1; } @@ -531,6 +606,23 @@ static int _dns_decode_head(struct dns_context *context) return -1; } + /* + 0 1 2 3 4 5 6 7 8 9 A B C D E F + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | ID | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |QR| Opcode |AA|TC|RD|RA| Z | RCODE | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | QDCOUNT | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | ANCOUNT | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | NSCOUNT | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | ARCOUNT | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ + head->id = dns_read_short(&context->ptr); fields = dns_read_short(&context->ptr); head->qr = (fields & QR_MASK) >> 15; @@ -584,6 +676,7 @@ static int _dns_decode_domain(struct dns_context *context, char *output, int siz unsigned char *ptr = context->ptr; int is_compressed = 0; + /*[len]string[len]string...[0]0 */ while (1) { if (ptr > context->data + context->maxsize || ptr < context->data) { return -1; @@ -595,35 +688,45 @@ static int _dns_decode_domain(struct dns_context *context, char *output, int siz break; } + /* compressed domain */ if (len >= 0xC0) { + /* + 0 1 2 3 4 5 6 7 8 9 A B C D E F + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1| OFFSET | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ + /* read offset */ len = dns_read_short(&ptr) & 0x3FFF; if (is_compressed == 0) { context->ptr = ptr; } ptr = context->data + len; if (context->maxsize - (ptr - context->data) < 0) { - tlog(TLOG_ERROR, "length is not enouth %d:%d, %p, %p", context->maxsize, ptr - context->data, context->ptr, context->data); + tlog(TLOG_ERROR, "length is not enouth %u:%ld, %p, %p", context->maxsize, (long)(ptr - context->data), context->ptr, context->data); return -1; } is_compressed = 1; continue; } + /* change [len] to '.' */ if (output_len > 0) { *output = '.'; output++; } if (context->maxsize - (ptr - context->data) < 0) { - tlog(TLOG_ERROR, "length is not enouth %d:%d, %p, %p", context->maxsize, ptr - context->data, context->ptr, context->data); + tlog(TLOG_ERROR, "length is not enouth %u:%ld, %p, %p", context->maxsize, (long)(ptr - context->data), context->ptr, context->data); return -1; } ptr++; if (output_len < size - 1) { + /* copy sub string */ copy_len = (len < size - output_len) ? len : size - 1 - output_len; if (context->maxsize - (ptr - context->data) < 0) { - tlog(TLOG_ERROR, "length is not enouth %d:%d, %p, %p", context->maxsize, ptr - context->data, context->ptr, context->data); + tlog(TLOG_ERROR, "length is not enouth %u:%ld, %p, %p", context->maxsize, (long)(ptr - context->data), context->ptr, context->data); return -1; } memcpy(output, ptr, copy_len); @@ -647,6 +750,7 @@ static int _dns_encode_domain(struct dns_context *context, char *domain) int total_len = 0; unsigned char *ptr_num = context->ptr++; + /*[len]string[len]string...[0]0 */ while (_dns_left_len(context) > 1 && *domain != 0) { if (*domain == '.') { *ptr_num = num; @@ -665,6 +769,7 @@ static int _dns_encode_domain(struct dns_context *context, char *domain) *ptr_num = num; if (total_len > 0) { + /* if domain is '\0', [domain] is '\0' */ *(context->ptr) = 0; context->ptr++; } @@ -674,7 +779,19 @@ static int _dns_encode_domain(struct dns_context *context, char *domain) static int _dns_decode_qr_head(struct dns_context *context, char *domain, int domain_size, int *qtype, int *qclass) { int ret = 0; - + /* + 0 1 2 3 4 5 6 7 8 9 A B C D E F + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | + / / + / NAME / + | | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | TYPE | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | CLASS | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ ret = _dns_decode_domain(context, domain, domain_size); if (ret < 0) { tlog(TLOG_ERROR, "decode domain failed."); @@ -758,6 +875,27 @@ static int _dns_encode_raw(struct dns_context *context, struct dns_rrs *rrs) char domain[DNS_MAX_CNAME_LEN]; int rr_len; struct dns_data_context data_context; + /* + 0 1 2 3 4 5 6 7 8 9 A B C D E F + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | + / / + / NAME / + | | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | TYPE | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | CLASS | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | TTL | + | | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | RDLENGTH | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| + / RDATA / + / / + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ data_context.data = rrs->data; data_context.ptr = rrs->data; @@ -968,6 +1106,7 @@ static int _dns_decode_an(struct dns_context *context, dns_rr_type type) struct dns_packet *packet = context->packet; unsigned char *start; + /* decode rr head */ ret = _dns_decode_rr_head(context, domain, DNS_MAX_CNAME_LEN, &qtype, &qclass, &ttl, &rr_len); if (ret < 0) { tlog(TLOG_ERROR, "decode head failed."); @@ -975,6 +1114,7 @@ static int _dns_decode_an(struct dns_context *context, dns_rr_type type) } start = context->ptr; + /* decode answer */ switch (qtype) { case DNS_T_A: { unsigned char addr[DNS_RR_A_LEN]; @@ -1067,7 +1207,7 @@ static int _dns_decode_an(struct dns_context *context, dns_rr_type type) } if (context->ptr - start != rr_len) { - tlog(TLOG_ERROR, "length mitchmatch , %s, %d:%d", domain, context->ptr - start, rr_len); + tlog(TLOG_ERROR, "length mitchmatch , %s, %ld:%d", domain, (long)(context->ptr - start), rr_len); return -1; } @@ -1297,3 +1437,35 @@ int dns_encode(unsigned char *data, int size, struct dns_packet *packet) return context.ptr - context.data; } + +void dns_debug(void) +{ + unsigned char data[1024]; + int len; + char buff[4096]; + + int fd = open("dns.bin", O_RDWR); + if (fd < 0) { + return; + } + len = read(fd, data, 1024); + close(fd); + if (len < 0) { + return; + } + + struct dns_packet *packet = (struct dns_packet *)buff; + if (dns_decode(packet, 4096, data, len) != 0) { + tlog(TLOG_ERROR, "decode failed.\n"); + } + + memset(data, 0, sizeof(data)); + len = dns_encode(data, 1024, packet); + if (len < 0) { + tlog(TLOG_ERROR, "encode failed.\n"); + } + + fd = open("dns-cmp.bin", O_CREAT | O_TRUNC | O_RDWR); + write(fd, data, len); + close(fd); +} diff --git a/dns.h b/src/dns.h similarity index 88% rename from dns.h rename to src/dns.h index a667a4c..6166e51 100644 --- a/dns.h +++ b/src/dns.h @@ -9,8 +9,8 @@ #define DNS_RR_A_LEN 4 #define DNS_RR_AAAA_LEN 16 #define DNS_MAX_CNAME_LEN 256 -#define DNS_IN_PACKSIZE (512 * 2) -#define DNS_PACKSIZE (512 * 4) +#define DNS_IN_PACKSIZE (512 * 4) +#define DNS_PACKSIZE (512 * 8) typedef enum dns_qr { DNS_QR_QUERY = 0, @@ -68,8 +68,9 @@ typedef enum dns_rtcode { DNS_RC_BADVERS = 16, } dns_rtcode_t; /* dns_rcode */ +/* dns packet head */ struct dns_head { - unsigned short id; // identification number + unsigned short id; /* identification number */ unsigned short qr; /* Query/Response Flag */ unsigned short opcode; /* Operation Code */ unsigned char aa; /* Authoritative Answer Flag */ @@ -77,10 +78,10 @@ struct dns_head { unsigned char rd; /* Recursion Desired */ unsigned char ra; /* Recursion Available */ unsigned short rcode; /* Response Code */ - unsigned short qdcount; // number of question entries - unsigned short ancount; // number of answer entries - unsigned short nscount; // number of authority entries - unsigned short nrcount; // number of addititional resource entries + unsigned short qdcount; /* number of question entries */ + unsigned short ancount; /* number of answer entries */ + unsigned short nscount; /* number of authority entries */ + unsigned short nrcount; /* number of addititional resource entries */ } __attribute__((packed)); struct dns_rrs { @@ -90,6 +91,7 @@ struct dns_rrs { unsigned char data[0]; }; +/* packet haed */ struct dns_packet { struct dns_head head; unsigned short questions; @@ -101,12 +103,14 @@ struct dns_packet { unsigned char data[0]; }; +/* RRS encode/decode context */ struct dns_data_context { unsigned char *data; unsigned char *ptr; unsigned int maxsize; }; +/* packet encode/decode context */ struct dns_context { struct dns_packet *packet; unsigned char *data; @@ -114,6 +118,7 @@ struct dns_context { unsigned char *ptr; }; +/* SOA data */ struct dns_soa { char mname[DNS_MAX_CNAME_LEN]; char rname[DNS_MAX_CNAME_LEN]; @@ -122,48 +127,41 @@ struct dns_soa { unsigned int retry; unsigned int expire; unsigned int minimum; -} __attribute__((packed));; +} __attribute__((packed)); +; struct dns_rrs *dns_get_rrs_next(struct dns_packet *packet, struct dns_rrs *rrs); - struct dns_rrs *dns_get_rrs_start(struct dns_packet *packet, dns_rr_type type, int *count); /* * Question */ int dns_add_domain(struct dns_packet *packet, char *domain, int qtype, int qclass); - int dns_get_domain(struct dns_rrs *rrs, char *domain, int maxsize, int *qtype, int *qclass); /* * Answers */ int dns_add_CNAME(struct dns_packet *packet, dns_rr_type type, char *domain, int ttl, char *cname); - int dns_get_CNAME(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, char *cname, int cname_size); int dns_add_A(struct dns_packet *packet, dns_rr_type type, char *domain, int ttl, unsigned char addr[DNS_RR_A_LEN]); - int dns_get_A(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, unsigned char addr[DNS_RR_A_LEN]); int dns_add_PTR(struct dns_packet *packet, dns_rr_type type, char *domain, int ttl, char *cname); - int dns_get_PTR(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, char *cname, int cname_size); int dns_add_AAAA(struct dns_packet *packet, dns_rr_type type, char *domain, int ttl, unsigned char addr[DNS_RR_AAAA_LEN]); - int dns_get_AAAA(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, unsigned char addr[DNS_RR_AAAA_LEN]); int dns_add_SOA(struct dns_packet *packet, dns_rr_type type, char *domain, int ttl, struct dns_soa *soa); - int dns_get_SOA(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, struct dns_soa *soa); /* * Packet operation */ int dns_decode(struct dns_packet *packet, int maxsize, unsigned char *data, int size); - int dns_encode(unsigned char *data, int size, struct dns_packet *packet); int dns_packet_init(struct dns_packet *packet, int size, struct dns_head *head); -#endif \ No newline at end of file +#endif diff --git a/dns_client.c b/src/dns_client.c similarity index 90% rename from dns_client.c rename to src/dns_client.c index f5c0431..233ac15 100644 --- a/dns_client.c +++ b/src/dns_client.c @@ -44,40 +44,41 @@ #include #define DNS_MAX_HOSTNAME 256 - #define DNS_MAX_EVENTS 64 - #define DNS_HOSTNAME_LEN 128 -struct dns_query_server { - int fd; - int type; - char host[DNS_HOSTNAME_LEN]; - struct list_head list; -}; - +/* dns client */ struct dns_client { pthread_t tid; int run; int epoll_fd; + /* dns server list */ pthread_mutex_t server_list_lock; struct list_head dns_server_list; + /* query list */ pthread_mutex_t dns_request_lock; struct list_head dns_request_list; - struct list_head dns_request_wait_list; + /* query doman hash table, key: sid + domain */ pthread_mutex_t domain_map_lock; DECLARE_HASHTABLE(domain_map, 6); + /* client socket */ int udp; }; +/* dns server information */ struct dns_server_info { struct list_head list; + /* server ping handle */ struct ping_host_struct *ping_host; + + /* server type */ dns_server_type_t type; + + /* server addr info */ unsigned short ss_family; socklen_t addr_len; union { @@ -87,6 +88,7 @@ struct dns_server_info { }; }; +/* dns replied server info */ struct dns_query_replied { struct hlist_node node; socklen_t addr_len; @@ -97,25 +99,36 @@ struct dns_query_replied { }; }; +/* query struct */ struct dns_query_struct { atomic_t refcnt; + /* query id, hash key sid + domain*/ + char domain[DNS_MAX_CNAME_LEN]; unsigned short sid; + struct hlist_node domain_node; + struct list_head dns_request_list; struct list_head period_list; - struct hlist_node domain_node; - char domain[DNS_MAX_CNAME_LEN]; - int qtype; - atomic_t dns_request_sent; - void *user_ptr; - unsigned long send_tick; - dns_client_callback callback; + /* dns query type */ + int qtype; + + /* dns query number */ + atomic_t dns_request_sent; + unsigned long send_tick; + + /* caller notification */ + dns_client_callback callback; + void *user_ptr; + + /* replied hash table */ DECLARE_HASHTABLE(replied_map, 4); }; static struct dns_client client; static atomic_t dns_client_sid = ATOMIC_INIT(0); +/* get addr info */ static struct addrinfo *_dns_client_getaddr(const char *host, char *port, int type, int protocol) { struct addrinfo hints; @@ -142,6 +155,7 @@ errout: return NULL; } +/* check whether server exists */ int _dns_client_server_exist(struct addrinfo *gai, dns_server_type_t server_type) { struct dns_server_info *server_info, *tmp; @@ -159,13 +173,16 @@ int _dns_client_server_exist(struct addrinfo *gai, dns_server_type_t server_type if (memcmp(&server_info->addr, gai->ai_addr, gai->ai_addrlen) != 0) { continue; } + pthread_mutex_lock(&client.server_list_lock); return 0; } + pthread_mutex_unlock(&client.server_list_lock); return -1; } +/* add dns server information */ int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type) { struct dns_server_info *server_info = NULL; @@ -183,16 +200,18 @@ int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_typ server_info->addr_len = gai->ai_addrlen; server_info->type = server_type; if (gai->ai_addrlen > sizeof(server_info->in6)) { - tlog(TLOG_ERROR, "addr len invalid, %d, %d, %d", gai->ai_addrlen, sizeof(server_info->addr), server_info->ss_family); + tlog(TLOG_ERROR, "addr len invalid, %d, %zd, %d", gai->ai_addrlen, sizeof(server_info->addr), server_info->ss_family); goto errout; } memcpy(&server_info->addr, gai->ai_addr, gai->ai_addrlen); + /* start ping task */ server_info->ping_host = fast_ping_start(server_ip, 0, 60000, 1000, NULL, server_info); if (server_info->ping_host == NULL) { goto errout; } + /* add to list */ pthread_mutex_lock(&client.server_list_lock); list_add(&server_info->list, &client.dns_server_list); pthread_mutex_unlock(&client.server_list_lock); @@ -208,6 +227,7 @@ errout: return -1; } +/* remove all servers information */ void _dns_client_server_remove_all(void) { struct dns_server_info *server_info, *tmp; @@ -215,6 +235,7 @@ void _dns_client_server_remove_all(void) list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list) { list_del(&server_info->list); + /* stop ping task */ if (fast_ping_stop(server_info->ping_host) != 0) { tlog(TLOG_ERROR, "stop ping failed.\n"); } @@ -223,9 +244,12 @@ void _dns_client_server_remove_all(void) pthread_mutex_unlock(&client.server_list_lock); } +/* remove single server */ int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type) { struct dns_server_info *server_info, *tmp; + + /* find server and remove */ pthread_mutex_lock(&client.server_list_lock); list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list) { @@ -265,6 +289,7 @@ int _dns_client_server_operate(char *server_ip, int port, dns_server_type_t serv sock_type = SOCK_STREAM; } + /* get addr info */ snprintf(port_s, 8, "%d", port); gai = _dns_client_getaddr(server_ip, port_s, sock_type, 0); if (gai == NULL) { @@ -317,10 +342,12 @@ void _dns_client_query_release(struct dns_query_struct *query) return; } + /* notify caller query end */ if (query->callback) { query->callback(query->domain, DNS_QUERY_END, NULL, NULL, 0, query->user_ptr); } + /* free resource */ pthread_mutex_lock(&client.domain_map_lock); list_del_init(&query->dns_request_list); hash_del(&query->domain_node); @@ -337,6 +364,7 @@ void _dns_client_query_release(struct dns_query_struct *query) void _dns_client_query_remove(struct dns_query_struct *query) { + /* remove query from period check list, and release reference*/ pthread_mutex_lock(&client.domain_map_lock); list_del_init(&query->dns_request_list); hash_del(&query->domain_node); @@ -371,12 +399,14 @@ void _dns_client_query_get(struct dns_query_struct *query) atomic_inc(&query->refcnt); } -void _dns_client_period_run() +void _dns_client_period_run(void) { struct dns_query_struct *query, *tmp; LIST_HEAD(check_list); unsigned long now = get_tick_count(); + + /* get query which timed out to check list */ pthread_mutex_lock(&client.domain_map_lock); list_for_each_entry_safe(query, tmp, &client.dns_request_list, dns_request_list) { @@ -389,6 +419,7 @@ void _dns_client_period_run() list_for_each_entry_safe(query, tmp, &check_list, period_list) { + /* free timed out query, and notify caller */ list_del_init(&query->period_list); _dns_client_query_remove(query); _dns_client_query_release(query); @@ -399,9 +430,11 @@ void _dns_client_period_run() static struct dns_query_struct *_dns_client_get_request(unsigned short sid, char *domain) { struct dns_query_struct *query = NULL; + struct dns_query_struct *query_result = NULL; struct hlist_node *tmp = NULL; unsigned int key; + /* get query by hash key : id + domain */ key = hash_string(domain); key = jhash(&sid, sizeof(sid), key); pthread_mutex_lock(&client.domain_map_lock); @@ -410,11 +443,16 @@ static struct dns_query_struct *_dns_client_get_request(unsigned short sid, char if (strncmp(query->domain, domain, DNS_MAX_CNAME_LEN) != 0) { continue; } + + if (sid != query->sid) { + continue; + } + query_result = query; break; } pthread_mutex_unlock(&client.domain_map_lock); - return query; + return query_result; } int _dns_replied_check_add(struct dns_query_struct *dns_query, struct sockaddr *addr, socklen_t addr_len) @@ -427,9 +465,11 @@ int _dns_replied_check_add(struct dns_query_struct *dns_query, struct sockaddr * return -1; } + /* avoid multiple replies from one server */ key = jhash(addr, addr_len, 0); hash_for_each_possible(dns_query->replied_map, replied_map, node, key) { + /* already replied, ignore this reply */ if (memcmp(&replied_map->addr, addr, addr_len) == 0) { return -1; } @@ -441,6 +481,7 @@ int _dns_replied_check_add(struct dns_query_struct *dns_query, struct sockaddr * return -1; } + /* add address info to check hashtable */ memcpy(&replied_map->addr, addr, addr_len); hash_add(dns_query->replied_map, &replied_map->node, key); return 0; @@ -462,12 +503,15 @@ static int _dns_client_recv(unsigned char *inpacket, int inpacket_len, struct so int request_num = 0; packet->head.tc = 0; + + /* decode domain from udp packet */ len = dns_decode(packet, DNS_PACKSIZE, inpacket, inpacket_len); if (len != 0) { tlog(TLOG_ERROR, "decode failed, packet len = %d, tc=%d, %d\n", inpacket_len, packet->head.tc, packet->head.id); return -1; } + /* not answer, return error */ if (packet->head.qr != DNS_OP_IQUERY) { tlog(TLOG_ERROR, "message type error.\n"); return -1; @@ -477,17 +521,20 @@ static int _dns_client_recv(unsigned char *inpacket, int inpacket_len, struct so packet->head.ancount, packet->head.nscount, packet->head.nrcount, inpacket_len, packet->head.id, packet->head.tc, packet->head.rd, packet->head.ra, packet->head.rcode); + /* get question */ rrs = dns_get_rrs_start(packet, DNS_RRS_QD, &rr_count); for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { dns_get_domain(rrs, domain, DNS_MAX_CNAME_LEN, &qtype, &qclass); tlog(TLOG_DEBUG, "domain: %s qtype: %d qclass: %d\n", domain, qtype, qclass); } + /* get query reference */ query = _dns_client_get_request(packet->head.id, domain); if (query == NULL) { return 0; } + /* avoid multiple replies */ if (_dns_replied_check_add(query, (struct sockaddr *)from, from_len) != 0) { return 0; } @@ -498,13 +545,15 @@ static int _dns_client_recv(unsigned char *inpacket, int inpacket_len, struct so return -1; } + /* notify caller dns query result */ if (query->callback) { ret = query->callback(query->domain, DNS_QUERY_RESULT, packet, inpacket, inpacket_len, query->user_ptr); } if (request_num == 0 || ret) { + /* if all server replied, or done, stop query, release resource */ _dns_client_query_remove(query); - } + } return ret; } @@ -517,6 +566,7 @@ static int _dns_client_process(struct dns_query_struct *dns_query, unsigned long socklen_t from_len = sizeof(from); char from_host[DNS_MAX_CNAME_LEN]; + /* receive from udp */ len = recvfrom(client.udp, inpacket, sizeof(inpacket), 0, (struct sockaddr *)&from, (socklen_t *)&from_len); if (len < 0) { tlog(TLOG_ERROR, "recvfrom failed, %s\n", strerror(errno)); @@ -526,9 +576,6 @@ static int _dns_client_process(struct dns_query_struct *dns_query, unsigned long tlog(TLOG_DEBUG, "recv from %s", gethost_by_addr(from_host, (struct sockaddr *)&from, from_len)); if (_dns_client_recv(inpacket, len, &from, from_len) != 0) { - int fd = open("dns.bin", O_CREAT | O_TRUNC | O_RDWR); - write(fd, inpacket, len); - close(fd); return -1; } @@ -590,21 +637,35 @@ static int _dns_client_send_udp(struct dns_server_info *server_info, void *packe return 0; } +static int _dns_client_send_tcp(struct dns_server_info *server_info, void *packet, int len) +{ + return -1; +} + + static int _dns_client_send_packet(struct dns_query_struct *query, void *packet, int len) { struct dns_server_info *server_info, *tmp; int ret = 0; query->send_tick = get_tick_count(); + + /* send query to all dns servers */ pthread_mutex_lock(&client.server_list_lock); list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list) { atomic_inc(&query->dns_request_sent); switch (server_info->type) { case DNS_SERVER_UDP: + /* udp query */ ret = _dns_client_send_udp(server_info, packet, len); break; + case DNS_SERVER_TCP: + /* tcp query */ + ret = _dns_client_send_tcp(server_info, packet, len); + break; default: + /* unsupport query type */ ret = -1; break; } @@ -627,6 +688,7 @@ static int _dns_client_send_query(struct dns_query_struct *query, char *doamin) struct dns_packet *packet = (struct dns_packet *)packet_buff; int encode_len; + /* init dns packet head */ struct dns_head head; memset(&head, 0, sizeof(head)); head.id = query->sid; @@ -638,13 +700,18 @@ static int _dns_client_send_query(struct dns_query_struct *query, char *doamin) head.rcode = 0; dns_packet_init(packet, DNS_PACKSIZE, &head); + + /* add question */ dns_add_domain(packet, doamin, query->qtype, DNS_C_IN); + + /* encode packet */ encode_len = dns_encode(inpacket, DNS_IN_PACKSIZE, packet); if (encode_len <= 0) { tlog(TLOG_ERROR, "encode query failed."); return -1; } + /* send query packet */ return _dns_client_send_packet(query, inpacket, encode_len); } @@ -659,6 +726,7 @@ int dns_client_query(char *domain, int qtype, dns_client_callback callback, void goto errout; } memset(query, 0, sizeof(*query)); + INIT_HLIST_NODE(&query->domain_node); INIT_LIST_HEAD(&query->dns_request_list); atomic_set(&query->refcnt, 0); @@ -672,6 +740,7 @@ int dns_client_query(char *domain, int qtype, dns_client_callback callback, void query->sid = atomic_inc_return(&dns_client_sid); _dns_client_query_get(query); + /* add query to hashtable */ key = hash_string(domain); key = jhash(&query->sid, sizeof(query->sid), key); pthread_mutex_lock(&client.domain_map_lock); @@ -679,6 +748,7 @@ int dns_client_query(char *domain, int qtype, dns_client_callback callback, void hash_add(client.domain_map, &query->domain_node, key); pthread_mutex_unlock(&client.domain_map_lock); + /* send query */ ret = _dns_client_send_query(query, domain); if (ret != 0) { goto errout_del_list; @@ -701,11 +771,6 @@ errout: return -1; } -int dns_client_query_raw(char *domain, int qtype, unsigned char *raw, int raw_len, void *user_ptr) -{ - return -1; -} - static struct addrinfo *_dns_server_getaddr(const char *host, const char *port, int type, int protocol) { struct addrinfo hints; @@ -748,6 +813,7 @@ int dns_client_socket(void) int fd = -1; struct addrinfo *gai = NULL; + /* create udp socket */ gai = _dns_server_getaddr(NULL, "53", SOCK_DGRAM, 0); if (gai == NULL) { tlog(TLOG_ERROR, "get address failed.\n"); @@ -775,38 +841,6 @@ errout: return -1; } -void dns_debug(void) -{ - unsigned char data[1024]; - int len; - char buff[4096]; - - int fd = open("dns.bin", O_RDWR); - if (fd < 0) { - return; - } - len = read(fd, data, 1024); - close(fd); - if (len < 0) { - return; - } - - struct dns_packet *packet = (struct dns_packet *)buff; - if (dns_decode(packet, 4096, data, len) != 0) { - tlog(TLOG_ERROR, "decode failed.\n"); - } - - memset(data, 0, sizeof(data)); - len = dns_encode(data, 1024, packet); - if (len < 0) { - tlog(TLOG_ERROR, "encode failed.\n"); - } - - fd = open("dns-cmp.bin", O_CREAT | O_TRUNC | O_RDWR); - write(fd, data, len); - close(fd); -} - int dns_client_init() { pthread_attr_t attr; @@ -838,12 +872,13 @@ int dns_client_init() pthread_mutex_init(&client.domain_map_lock, 0); hash_init(client.domain_map); - INIT_LIST_HEAD(&client.dns_request_wait_list); INIT_LIST_HEAD(&client.dns_request_list); client.epoll_fd = epollfd; client.run = 1; client.udp = fd; + + /* start work task */ ret = pthread_create(&client.tid, &attr, _dns_client_work, NULL); if (ret != 0) { tlog(TLOG_ERROR, "create client work thread failed, %s\n", strerror(errno)); @@ -890,6 +925,7 @@ void dns_client_exit() close(client.udp); } + /* free all resouces */ _dns_client_server_remove_all(); _dns_client_query_remove_all(); diff --git a/dns_client.h b/src/dns_client.h similarity index 87% rename from dns_client.h rename to src/dns_client.h index 1864e44..24238ac 100644 --- a/dns_client.h +++ b/src/dns_client.h @@ -18,16 +18,18 @@ typedef enum dns_result_type { int dns_client_init(void); +/* query result notify function */ typedef int (*dns_client_callback)(char *domain, dns_result_type rtype, struct dns_packet *packet, unsigned char *inpacket, int inpacket_len, void *user_ptr); +/* query domain */ int dns_client_query(char *domain, int qtype, dns_client_callback callback, void *user_ptr); -int dns_client_query_raw(char *domain, int qtype, unsigned char *raw, int raw_len, void *user_ptr); - void dns_client_exit(void); +/* add remote dns server */ int dns_add_server(char *server_ip, int port, dns_server_type_t server_type); +/* remove remote dns server */ int dns_remove_server(char *server_ip, int port, dns_server_type_t server_type); #endif diff --git a/dns_server.c b/src/dns_server.c similarity index 98% rename from dns_server.c rename to src/dns_server.c index f0049db..aade10f 100644 --- a/dns_server.c +++ b/src/dns_server.c @@ -49,16 +49,18 @@ #define DNS_MAX_EVENTS 256 +/* dns server data */ struct dns_server { int run; int epoll_fd; - int fd; + /* dns request list */ pthread_mutex_t request_list_lock; struct list_head request_list; }; +/* ip address lists of domain */ struct dns_ip_address { struct hlist_node node; dns_type_t addr_type; @@ -71,9 +73,13 @@ struct dns_ip_address { struct dns_request { atomic_t refcnt; + /* dns request list */ struct list_head list; + + /* dns request timeout check list */ struct list_head check_list; + /* dns query */ char domain[DNS_MAX_CNAME_LEN]; struct dns_head head; unsigned long send_tick; @@ -90,7 +96,6 @@ struct dns_request { int has_ping_result; int has_ping_tcp; - int has_ptr; int has_cname; @@ -109,6 +114,7 @@ struct dns_request { atomic_t notified; + /* send original raw packet to server/client like proxy */ int passthrough; pthread_mutex_t ip_map_lock; @@ -464,6 +470,10 @@ static int _dns_server_process_answer(struct dns_request *request, char *domain, switch (rrs->type) { case DNS_T_A: { unsigned char addr[4]; + if (request->qtype != DNS_T_A) { + /* ignore non-matched query type */ + break; + } _dns_server_request_get(request); dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); @@ -496,6 +506,10 @@ static int _dns_server_process_answer(struct dns_request *request, char *domain, } break; case DNS_T_AAAA: { unsigned char addr[16]; + if (request->qtype != DNS_T_AAAA) { + /* ignore non-matched query type */ + break; + } _dns_server_request_get(request); dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); @@ -808,7 +822,7 @@ void _dns_server_tcp_ping_check(struct dns_request *request) request->has_ping_tcp = 1; } -void _dns_server_period_run() +void _dns_server_period_run(void) { struct dns_request *request, *tmp; LIST_HEAD(check_list); diff --git a/dns_server.h b/src/dns_server.h similarity index 100% rename from dns_server.h rename to src/dns_server.h diff --git a/fast_ping.c b/src/fast_ping.c old mode 100755 new mode 100644 similarity index 95% rename from fast_ping.c rename to src/fast_ping.c index 07acc24..f9751b4 --- a/fast_ping.c +++ b/src/fast_ping.c @@ -891,7 +891,7 @@ static void _fast_ping_remove_all(void) } } -static void _fast_ping_period_run() +static void _fast_ping_period_run(void) { struct ping_host_struct *ping_host = NULL; struct ping_host_struct *ping_host_tmp = NULL; @@ -1002,7 +1002,7 @@ static void *_fast_ping_work(void *arg) return NULL; } -int fast_ping_init() +int fast_ping_init(void) { pthread_attr_t attr; int epollfd = -1; @@ -1051,7 +1051,7 @@ errout: return -1; } -void fast_ping_exit() +void fast_ping_exit(void) { if (ping.tid > 0) { void *ret = NULL; diff --git a/fast_ping.h b/src/fast_ping.h old mode 100755 new mode 100644 similarity index 86% rename from fast_ping.h rename to src/fast_ping.h index cbc56fa..516292b --- a/fast_ping.h +++ b/src/fast_ping.h @@ -23,13 +23,15 @@ typedef enum { struct ping_host_struct; typedef void (*fast_ping_result)(struct ping_host_struct *ping_host, const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, int seqno, struct timeval *tv, void *userptr); +/* start ping */ struct ping_host_struct *fast_ping_start(const char *host, int count, int interval, int timeout, fast_ping_result ping_callback, void *userptr); +/* stop ping */ int fast_ping_stop(struct ping_host_struct *ping_host); -int fast_ping_init(); +int fast_ping_init(void); -void fast_ping_exit(); +void fast_ping_exit(void); #ifdef __cpluscplus } diff --git a/include/atomic.h b/src/include/atomic.h old mode 100755 new mode 100644 similarity index 100% rename from include/atomic.h rename to src/include/atomic.h diff --git a/include/bitmap.h b/src/include/bitmap.h similarity index 100% rename from include/bitmap.h rename to src/include/bitmap.h diff --git a/include/bitops.h b/src/include/bitops.h similarity index 100% rename from include/bitops.h rename to src/include/bitops.h diff --git a/include/cache.h b/src/include/cache.h similarity index 100% rename from include/cache.h rename to src/include/cache.h diff --git a/include/findbit.h b/src/include/findbit.h similarity index 100% rename from include/findbit.h rename to src/include/findbit.h diff --git a/include/gcc_builtin.h b/src/include/gcc_builtin.h similarity index 100% rename from include/gcc_builtin.h rename to src/include/gcc_builtin.h diff --git a/include/hash.h b/src/include/hash.h similarity index 100% rename from include/hash.h rename to src/include/hash.h diff --git a/include/hashtable.h b/src/include/hashtable.h similarity index 100% rename from include/hashtable.h rename to src/include/hashtable.h diff --git a/include/jhash.h b/src/include/jhash.h similarity index 100% rename from include/jhash.h rename to src/include/jhash.h diff --git a/include/list.h b/src/include/list.h similarity index 100% rename from include/list.h rename to src/include/list.h diff --git a/lib/bitops.c b/src/lib/bitops.c similarity index 100% rename from lib/bitops.c rename to src/lib/bitops.c diff --git a/lib/cache.c b/src/lib/cache.c similarity index 100% rename from lib/cache.c rename to src/lib/cache.c diff --git a/smartdns.c b/src/smartdns.c old mode 100755 new mode 100644 similarity index 59% rename from smartdns.c rename to src/smartdns.c index 9b95644..9d3f8f9 --- a/smartdns.c +++ b/src/smartdns.c @@ -26,14 +26,36 @@ #include "tlog.h" #include "util.h" #include +#include #include #include #include #include +#include +#include #define RESOLVE_FILE "/etc/resolv.conf" #define MAX_LINE_LEN 1024 #define MAX_KEY_LEN 64 +#define SMARTDNS_CONF_FILE "/etc/smartdns/smartdns.conf" +#define SMARTDNS_LOG_PATH "/var/log" +#define SMARTDNS_LOG_FILE "smartdns.log" +#define SMARTDNS_PID_FILE "/var/run/smartdns.pid" +#define TMP_BUFF_LEN_32 32 + +void help(void) +{ + /* clang-format off */ + char *help = "" + "Usage: smartdns [OPTION]...\n" + "Start smartdns server.\n" + " -f run forground.\n" + " -c [conf] config file.\n" + " -h show this help message.\n" + "\n"; + /* clang-format on */ + printf(help); +} int smartdns_load_from_resolv(void) { @@ -101,15 +123,56 @@ int smartdns_add_servers(void) return 0; } -int smartdns_init() +int create_pid_file(const char *pid_file) +{ + int fd; + int flags; + char buff[TMP_BUFF_LEN_32]; + + /* create pid file, and lock this file */ + fd = open(pid_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) { + fprintf(stderr, "create pid file failed, %s", strerror(errno)); + return -1; + } + + flags = fcntl(fd, F_GETFD); + if (flags < 0) { + fprintf(stderr, "Could not get flags for PID file %s", pid_file); + goto errout; + } + + flags |= FD_CLOEXEC; + if (fcntl(fd, F_SETFD, flags) == -1) { + fprintf(stderr, "Could not set flags for PID file %s", pid_file); + goto errout; + } + + if (lockf(fd, F_TLOCK, 0) < 0) { + fprintf(stderr, "Server is already running.\n"); + goto errout; + } + + snprintf(buff, TMP_BUFF_LEN_32, "%d\n", getpid()); + + if (write(fd, buff, strnlen(buff, TMP_BUFF_LEN_32)) < 0) { + fprintf(stderr, "write pid to file failed, %s.\n", strerror(errno)); + goto errout; + } + + return 0; +errout: + if (fd > 0) { + close(fd); + } + return -1; +} + +int smartdns_init(void) { int ret; - if (load_conf("smartdns.conf") != 0) { - fprintf(stderr, "load config failed."); - } - - ret = tlog_init(".", "smartdns.log", 1024 * 1024, 8, 1, 0, 0); + ret = tlog_init(SMARTDNS_LOG_PATH, SMARTDNS_LOG_FILE, 1024 * 1024, 8, 1, 0, 0); if (ret != 0) { tlog(TLOG_ERROR, "start tlog failed.\n"); goto errout; @@ -154,12 +217,12 @@ errout: return -1; } -int smartdns_run() +int smartdns_run(void) { return dns_server_run(); } -void smartdns_exit() +void smartdns_exit(void) { dns_server_exit(); dns_client_exit(); @@ -185,11 +248,46 @@ void sig_handle(int sig) int main(int argc, char *argv[]) { int ret; + int is_forground = 0; + int opt; + char config_file[MAX_LINE_LEN]; + + strncpy(config_file, SMARTDNS_CONF_FILE, MAX_LINE_LEN); + + while ((opt = getopt(argc, argv, "fhc:")) != -1) { + switch (opt) { + case 'f': + is_forground = 1; + break; + case 'c': + snprintf(config_file, sizeof(config_file), optarg); + break; + case 'h': + help(); + return 1; + } + } + + if (is_forground == 0) { + if (daemon(0, 0) < 0) { + fprintf(stderr, "run daemon process failed, %s\n", strerror(errno)); + return 1; + } + } signal(SIGABRT, sig_handle); - + + if (load_conf(config_file) != 0) { + } + + if (create_pid_file(SMARTDNS_PID_FILE) != 0) { + fprintf(stderr, "create pid file failed, %s\n", strerror(errno)); + goto errout; + } + ret = smartdns_init(); if (ret != 0) { + usleep(100000); goto errout; } diff --git a/tlog.c b/src/tlog.c similarity index 100% rename from tlog.c rename to src/tlog.c diff --git a/tlog.h b/src/tlog.h similarity index 100% rename from tlog.h rename to src/tlog.h diff --git a/util.c b/src/util.c similarity index 98% rename from util.c rename to src/util.c index 7c130d5..ad4afde 100644 --- a/util.c +++ b/src/util.c @@ -5,7 +5,7 @@ #include #include -unsigned long get_tick_count() +unsigned long get_tick_count(void) { struct timespec ts; diff --git a/util.h b/src/util.h similarity index 88% rename from util.h rename to src/util.h index 1745016..93b4930 100644 --- a/util.h +++ b/src/util.h @@ -8,7 +8,7 @@ #define PORT_NOT_DEFINED -1 #define MAX_IP_LEN 64 -unsigned long get_tick_count(); +unsigned long get_tick_count(void); char *gethost_by_addr(char *host, struct sockaddr *addr, socklen_t addr_len); diff --git a/systemd/smartdns.service b/systemd/smartdns.service new file mode 100644 index 0000000..bd6bde0 --- /dev/null +++ b/systemd/smartdns.service @@ -0,0 +1,14 @@ +[Unit] +Description=smart dns server +After=network.target + +[Service] +PIDFile=/var/run/smartdns.pid +EnvironmentFile=/etc/default/smartdns +ExecStart=/usr/sbin/smartdns $SMART_DNS_OPTS +KillMode=process +Restart=on-failure + +[Install] +WantedBy=multi-user.target +Alias=smartdns.service diff --git a/test-dns.c b/test-dns.c deleted file mode 100755 index 4ae574a..0000000 --- a/test-dns.c +++ /dev/null @@ -1,755 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BUF_SIZE 1500 - -/* -* This software is licensed under the CC0. -* -* This is a _basic_ DNS Server for educational use. -* It does not prevent invalid packets from crashing -* the server. -* -* To test start the program and issue a DNS request: -* dig @127.0.0.1 -p 9000 foo.bar.com -*/ - - -/* -* Masks and constants. -*/ - -static const uint32_t QR_MASK = 0x8000; -static const uint32_t OPCODE_MASK = 0x7800; -static const uint32_t AA_MASK = 0x0400; -static const uint32_t TC_MASK = 0x0200; -static const uint32_t RD_MASK = 0x0100; -static const uint32_t RA_MASK = 0x8000; -static const uint32_t RCODE_MASK = 0x000F; - -/* Response Type */ -enum { - Ok_ResponseType = 0, - FormatError_ResponseType = 1, - ServerFailure_ResponseType = 2, - NameError_ResponseType = 3, - NotImplemented_ResponseType = 4, - Refused_ResponseType = 5 -}; - -/* Resource Record Types */ -enum { - A_Resource_RecordType = 1, - NS_Resource_RecordType = 2, - CNAME_Resource_RecordType = 5, - SOA_Resource_RecordType = 6, - PTR_Resource_RecordType = 12, - MX_Resource_RecordType = 15, - TXT_Resource_RecordType = 16, - AAAA_Resource_RecordType = 28, - SRV_Resource_RecordType = 33 -}; - -/* Operation Code */ -enum { - QUERY_OperationCode = 0, /* standard query */ - IQUERY_OperationCode = 1, /* inverse query */ - STATUS_OperationCode = 2, /* server status request */ - NOTIFY_OperationCode = 4, /* request zone transfer */ - UPDATE_OperationCode = 5 /* change resource records */ -}; - -/* Response Code */ -enum { - NoError_ResponseCode = 0, - FormatError_ResponseCode = 1, - ServerFailure_ResponseCode = 2, - NameError_ResponseCode = 3 -}; - -/* Query Type */ -enum { - IXFR_QueryType = 251, - AXFR_QueryType = 252, - MAILB_QueryType = 253, - MAILA_QueryType = 254, - STAR_QueryType = 255 -}; - -/* -* Types. -*/ - -/* Question Section */ -struct Question { - char *qName; - uint16_t qType; - uint16_t qClass; - struct Question* next; // for linked list -}; - -/* Data part of a Resource Record */ -union ResourceData { - struct { - char *txt_data; - } txt_record; - struct { - uint8_t addr[4]; - } a_record; - struct { - char* MName; - char* RName; - uint32_t serial; - uint32_t refresh; - uint32_t retry; - uint32_t expire; - uint32_t minimum; - } soa_record; - struct { - char *name; - } name_server_record; - struct { - char name; - } cname_record; - struct { - char *name; - } ptr_record; - struct { - uint16_t preference; - char *exchange; - } mx_record; - struct { - uint8_t addr[16]; - } aaaa_record; - struct { - uint16_t priority; - uint16_t weight; - uint16_t port; - char *target; - } srv_record; -}; - -/* Resource Record Section */ -struct ResourceRecord { - char *name; - uint16_t type; - uint16_t class; - uint16_t ttl; - uint16_t rd_length; - union ResourceData rd_data; - struct ResourceRecord* next; // for linked list -}; - -struct Message { - uint16_t id; /* Identifier */ - - /* Flags */ - uint16_t qr; /* Query/Response Flag */ - uint16_t opcode; /* Operation Code */ - uint16_t aa; /* Authoritative Answer Flag */ - uint16_t tc; /* Truncation Flag */ - uint16_t rd; /* Recursion Desired */ - uint16_t ra; /* Recursion Available */ - uint16_t rcode; /* Response Code */ - - uint16_t qdCount; /* Question Count */ - uint16_t anCount; /* Answer Record Count */ - uint16_t nsCount; /* Authority Record Count */ - uint16_t arCount; /* Additional Record Count */ - - /* At least one question; questions are copied to the response 1:1 */ - struct Question* questions; - - /* - * Resource records to be send back. - * Every resource record can be in any of the following places. - * But every place has a different semantic. - */ - struct ResourceRecord* answers; - struct ResourceRecord* authorities; - struct ResourceRecord* additionals; -}; - -int get_A_Record(uint8_t addr[4], const char domain_name[]) -{ - if (strcmp("foo.bar.com", domain_name) == 0) - { - addr[0] = 192; - addr[1] = 168; - addr[2] = 1; - addr[3] = 1; - return 0; - } - else - { - return -1; - } -} - -int get_AAAA_Record(uint8_t addr[16], const char domain_name[]) -{ - if (strcmp("foo.bar.com", domain_name) == 0) - { - addr[0] = 0xfe; - addr[1] = 0x80; - addr[2] = 0x00; - addr[3] = 0x00; - addr[4] = 0x00; - addr[5] = 0x00; - addr[6] = 0x00; - addr[7] = 0x00; - addr[8] = 0x00; - addr[9] = 0x00; - addr[10] = 0x00; - addr[11] = 0x00; - addr[12] = 0x00; - addr[13] = 0x00; - addr[14] = 0x00; - addr[15] = 0x01; - return 0; - } - else - { - return -1; - } -} - - -/* -* Debugging functions. -*/ - -void print_hex(uint8_t* buf, size_t len) -{ - int i; - printf("%zu bytes:\n", len); - for(i = 0; i < len; ++i) - printf("%02x ", buf[i]); - printf("\n"); -} - -void print_resource_record(struct ResourceRecord* rr) -{ - int i; - while (rr) - { - printf(" ResourceRecord { name '%s', type %u, class %u, ttl %u, rd_length %u, ", - rr->name, - rr->type, - rr->class, - rr->ttl, - rr->rd_length - ); - - union ResourceData *rd = &rr->rd_data; - switch (rr->type) - { - case A_Resource_RecordType: - printf("Address Resource Record { address "); - - for(i = 0; i < 4; ++i) - printf("%s%u", (i ? "." : ""), rd->a_record.addr[i]); - - printf(" }"); - break; - case NS_Resource_RecordType: - printf("Name Server Resource Record { name %s }", - rd->name_server_record.name - ); - break; - case CNAME_Resource_RecordType: - printf("Canonical Name Resource Record { name %u }", - rd->cname_record.name - ); - break; - case SOA_Resource_RecordType: - printf("SOA { MName '%s', RName '%s', serial %u, refresh %u, retry %u, expire %u, minimum %u }", - rd->soa_record.MName, - rd->soa_record.RName, - rd->soa_record.serial, - rd->soa_record.refresh, - rd->soa_record.retry, - rd->soa_record.expire, - rd->soa_record.minimum - ); - break; - case PTR_Resource_RecordType: - printf("Pointer Resource Record { name '%s' }", - rd->ptr_record.name - ); - break; - case MX_Resource_RecordType: - printf("Mail Exchange Record { preference %u, exchange '%s' }", - rd->mx_record.preference, - rd->mx_record.exchange - ); - break; - case TXT_Resource_RecordType: - printf("Text Resource Record { txt_data '%s' }", - rd->txt_record.txt_data - ); - break; - case AAAA_Resource_RecordType: - printf("AAAA Resource Record { address "); - - for(i = 0; i < 16; ++i) - printf("%s%02x", (i ? ":" : ""), rd->aaaa_record.addr[i]); - - printf(" }"); - break; - default: - printf("Unknown Resource Record { ??? }"); - } - printf("}\n"); - rr = rr->next; - } -} - -void print_query(struct Message* msg) -{ - printf("QUERY { ID: %02x", msg->id); - printf(". FIELDS: [ QR: %u, OpCode: %u ]", msg->qr, msg->opcode); - printf(", QDcount: %u", msg->qdCount); - printf(", ANcount: %u", msg->anCount); - printf(", NScount: %u", msg->nsCount); - printf(", ARcount: %u,\n", msg->arCount); - - struct Question* q = msg->questions; - while (q) - { - printf(" Question { qName '%s', qType %u, qClass %u }\n", - q->qName, - q->qType, - q->qClass - ); - q = q->next; - } - - print_resource_record(msg->answers); - print_resource_record(msg->authorities); - print_resource_record(msg->additionals); - - printf("}\n"); -} - - -/* -* Basic memory operations. -*/ - -size_t get16bits(const uint8_t** buffer) -{ - uint16_t value; - - memcpy(&value, *buffer, 2); - *buffer += 2; - - return ntohs(value); -} - -void put8bits(uint8_t** buffer, uint8_t value) -{ - memcpy(*buffer, &value, 1); - *buffer += 1; -} - -void put16bits(uint8_t** buffer, uint16_t value) -{ - value = htons(value); - memcpy(*buffer, &value, 2); - *buffer += 2; -} - -void put32bits(uint8_t** buffer, uint32_t value) -{ - value = htons(value); - memcpy(*buffer, &value, 4); - *buffer += 4; -} - - -/* -* Deconding/Encoding functions. -*/ - -// 3foo3bar3com0 => foo.bar.com -char* decode_domain_name(const uint8_t** buffer) -{ - char name[256]; - const uint8_t* buf = *buffer; - int j = 0; - int i = 0; - - while (buf[i] != 0) - { - //if (i >= buflen || i > sizeof(name)) - // return NULL; - - if (i != 0) - { - name[j] = '.'; - j += 1; - } - - int len = buf[i]; - i += 1; - - memcpy(name+j, buf+i, len); - i += len; - j += len; - } - - name[j] = '\0'; - - *buffer += i + 1; //also jump over the last 0 - - return strdup(name); -} - -// foo.bar.com => 3foo3bar3com0 -void encode_domain_name(uint8_t** buffer, const char* domain) -{ - uint8_t* buf = *buffer; - const char* beg = domain; - const char* pos; - int len = 0; - int i = 0; - - while ((pos = strchr(beg, '.'))) - { - len = pos - beg; - buf[i] = len; - i += 1; - memcpy(buf+i, beg, len); - i += len; - - beg = pos + 1; - } - - len = strlen(domain) - (beg - domain); - - buf[i] = len; - i += 1; - - memcpy(buf + i, beg, len); - i += len; - - buf[i] = 0; - i += 1; - - *buffer += i; -} - - -void decode_header(struct Message* msg, const uint8_t** buffer) -{ - msg->id = get16bits(buffer); - - uint32_t fields = get16bits(buffer); - msg->qr = (fields & QR_MASK) >> 15; - msg->opcode = (fields & OPCODE_MASK) >> 11; - msg->aa = (fields & AA_MASK) >> 10; - msg->tc = (fields & TC_MASK) >> 9; - msg->rd = (fields & RD_MASK) >> 8; - msg->ra = (fields & RA_MASK) >> 7; - msg->rcode = (fields & RCODE_MASK) >> 0; - - msg->qdCount = get16bits(buffer); - msg->anCount = get16bits(buffer); - msg->nsCount = get16bits(buffer); - msg->arCount = get16bits(buffer); -} - -void encode_header(struct Message* msg, uint8_t** buffer) -{ - put16bits(buffer, msg->id); - - int fields = 0; - fields |= (msg->qr << 15) & QR_MASK; - fields |= (msg->rcode << 0) & RCODE_MASK; - // TODO: insert the rest of the fields - put16bits(buffer, fields); - - put16bits(buffer, msg->qdCount); - put16bits(buffer, msg->anCount); - put16bits(buffer, msg->nsCount); - put16bits(buffer, msg->arCount); -} - -int decode_msg(struct Message* msg, const uint8_t* buffer, int size) -{ - int i; - - decode_header(msg, &buffer); - - if (msg->anCount != 0 || msg->nsCount != 0) - { - printf("Only questions expected!\n"); - return -1; - } - - // parse questions - uint32_t qcount = msg->qdCount; - struct Question* qs = msg->questions; - for (i = 0; i < qcount; ++i) - { - struct Question* q = malloc(sizeof(struct Question)); - - q->qName = decode_domain_name(&buffer); - q->qType = get16bits(&buffer); - q->qClass = get16bits(&buffer); - - // prepend question to questions list - q->next = qs; - msg->questions = q; - } - - // We do not expect any resource records to parse here. - - return 0; -} - -// For every question in the message add a appropiate resource record -// in either section 'answers', 'authorities' or 'additionals'. -void resolver_process(struct Message* msg) -{ - struct ResourceRecord* beg; - struct ResourceRecord* rr; - struct Question* q; - int rc; - - // leave most values intact for response - msg->qr = 1; // this is a response - msg->aa = 1; // this server is authoritative - msg->ra = 0; // no recursion available - msg->rcode = Ok_ResponseType; - - // should already be 0 - msg->anCount = 0; - msg->nsCount = 0; - msg->arCount = 0; - - // for every question append resource records - q = msg->questions; - while (q) - { - rr = malloc(sizeof(struct ResourceRecord)); - memset(rr, 0, sizeof(struct ResourceRecord)); - - rr->name = strdup(q->qName); - rr->type = q->qType; - rr->class = q->qClass; - rr->ttl = 60*60; // in seconds; 0 means no caching - - printf("Query for '%s'\n", q->qName); - - // We only can only answer two question types so far - // and the answer (resource records) will be all put - // into the answers list. - // This behavior is probably non-standard! - switch (q->qType) - { - case A_Resource_RecordType: - rr->rd_length = 4; - rc = get_A_Record(rr->rd_data.a_record.addr, q->qName); - if (rc < 0) - { - free(rr->name); - free(rr); - goto next; - } - break; - case AAAA_Resource_RecordType: - rr->rd_length = 16; - rc = get_AAAA_Record(rr->rd_data.aaaa_record.addr, q->qName); - if (rc < 0) - { - free(rr->name); - free(rr); - goto next; - } - break; - /* - case NS_Resource_RecordType: - case CNAME_Resource_RecordType: - case SOA_Resource_RecordType: - case PTR_Resource_RecordType: - case MX_Resource_RecordType: - case TXT_Resource_RecordType: - */ - default: - free(rr); - msg->rcode = NotImplemented_ResponseType; - printf("Cannot answer question of type %d.\n", q->qType); - goto next; - } - - msg->anCount++; - - // prepend resource record to answers list - beg = msg->answers; - msg->answers = rr; - rr->next = beg; - - // jump here to omit question - next: - - // process next question - q = q->next; - } -} - -/* @return 0 upon failure, 1 upon success */ -int encode_resource_records(struct ResourceRecord* rr, uint8_t** buffer) -{ - int i; - while (rr) - { - // Answer questions by attaching resource sections. - encode_domain_name(buffer, rr->name); - put16bits(buffer, rr->type); - put16bits(buffer, rr->class); - put32bits(buffer, rr->ttl); - put16bits(buffer, rr->rd_length); - - switch (rr->type) - { - case A_Resource_RecordType: - for(i = 0; i < 4; ++i) - put8bits(buffer, rr->rd_data.a_record.addr[i]); - break; - case AAAA_Resource_RecordType: - for(i = 0; i < 16; ++i) - put8bits(buffer, rr->rd_data.aaaa_record.addr[i]); - break; - default: - fprintf(stderr, "Unknown type %u. => Ignore resource record.\n", rr->type); - return 1; - } - - rr = rr->next; - } - - return 0; -} - -/* @return 0 upon failure, 1 upon success */ -int encode_msg(struct Message* msg, uint8_t** buffer) -{ - struct Question* q; - int rc; - - encode_header(msg, buffer); - - q = msg->questions; - while (q) - { - encode_domain_name(buffer, q->qName); - put16bits(buffer, q->qType); - put16bits(buffer, q->qClass); - - q = q->next; - } - - rc = 0; - rc |= encode_resource_records(msg->answers, buffer); - rc |= encode_resource_records(msg->authorities, buffer); - rc |= encode_resource_records(msg->additionals, buffer); - - return rc; -} - -void free_resource_records(struct ResourceRecord* rr) -{ - struct ResourceRecord* next; - - while (rr) { - free(rr->name); - next = rr->next; - free(rr); - rr = next; - } -} - -void free_questions(struct Question* qq) -{ - struct Question* next; - - while (qq) { - free(qq->qName); - next = qq->next; - free(qq); - qq = next; - } -} - -int main() -{ - // buffer for input/output binary packet - uint8_t buffer[BUF_SIZE]; - struct sockaddr_in client_addr; - socklen_t addr_len = sizeof(struct sockaddr_in); - struct sockaddr_in addr; - int nbytes, rc; - int sock; - int port = 53; - - struct Message msg; - memset(&msg, 0, sizeof(struct Message)); - - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons(port); - - sock = socket(AF_INET, SOCK_DGRAM, 0); - - rc = bind(sock, (struct sockaddr*) &addr, addr_len); - - if (rc != 0) - { - printf("Could not bind: %s\n", strerror(errno)); - return 1; - } - - printf("Listening on port %u.\n", port); - - while (1) - { - free_questions(msg.questions); - free_resource_records(msg.answers); - free_resource_records(msg.authorities); - free_resource_records(msg.additionals); - memset(&msg, 0, sizeof(struct Message)); - - nbytes = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &client_addr, &addr_len); - - if (decode_msg(&msg, buffer, nbytes) != 0) { - continue; - } - - /* Print query */ - print_query(&msg); - - resolver_process(&msg); - - /* Print response */ - print_query(&msg); - - uint8_t *p = buffer; - if (encode_msg(&msg, &p) != 0) { - continue; - } - - int buflen = p - buffer; - sendto(sock, buffer, buflen, 0, (struct sockaddr*) &client_addr, addr_len); - } -}