소스 검색

initial commit

Nick Peng 7 년 전
부모
커밋
c6af293927
15개의 변경된 파일3432개의 추가작업 그리고 0개의 파일을 삭제
  1. 9 0
      .clang-format
  2. 4 0
      .gitignore
  3. 15 0
      Makefile
  4. 19 0
      ReadMe.md
  5. 750 0
      fast_ping.c
  6. 31 0
      fast_ping.h
  7. 133 0
      include/atomic.h
  8. 132 0
      include/bitmap.h
  9. 65 0
      include/bitops.h
  10. 201 0
      include/hash.h
  11. 153 0
      include/hashtable.h
  12. 184 0
      include/jhash.h
  13. 792 0
      include/list.h
  14. 189 0
      smartdns.c
  15. 755 0
      test-dns.c

+ 9 - 0
.clang-format

@@ -0,0 +1,9 @@
+#http://clang.llvm.org/docs/ClangFormatStyleOptions.html
+
+BasedOnStyle: LLVM
+IndentWidth: 4
+TabWidth: 4
+UseTab: ForContinuationAndIndentation
+MaxEmptyLinesToKeep: 1
+AllowShortFunctionsOnASingleLine: Empty
+BreakBeforeBraces: Linux

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+.vscode
+.o
+.DS_Store
+.swp.

+ 15 - 0
Makefile

@@ -0,0 +1,15 @@
+
+BIN=smartdns 
+OBJS=smartdns.o fast_ping.o 
+CFLAGS=-g -O0
+CFLAGS=-Iinclude
+
+.PHONY: all
+
+all: $(BIN)
+
+$(BIN) : $(OBJS)
+	$(CC) $(OBJS) -o $@ -lpthread
+
+clean:
+	$(RM) $(OBJS) $(BIN)

+ 19 - 0
ReadMe.md

@@ -0,0 +1,19 @@
+Smart DNS
+==============
+Smard DNS会根据上级DNS服务返回的IP地址进行检查,如果IP地址无效,则继续进行DNS请求。
+
+
+特性
+--------------
+1. 多级DNS配置
+
+
+使用
+==============
+
+
+License
+===============
+GPL V2 License
+
+

+ 750 - 0
fast_ping.c

@@ -0,0 +1,750 @@
+#include "fast_ping.h"
+#include "atomic.h"
+#include "hashtable.h"
+#include <errno.h>
+#include <linux/filter.h>
+#include <netdb.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define PING_MAX_EVENTS 128
+#define PING_MAX_HOSTLEN 128
+#define ICMP_PACKET_SIZE (1024 * 64)
+#define ICMP_INPACKET_SIZE 1024
+
+struct fast_ping_packet_msg {
+    struct timeval tv;
+};
+
+struct fast_ping_packet {
+    union {
+        struct icmp icmp;
+        struct icmp6_hdr icmp6;
+    };
+    struct fast_ping_packet_msg msg;
+};
+
+struct ping_host_struct {
+    atomic_t ref;
+    struct hlist_node host_node;
+    struct hlist_node addr_node;
+    int type;
+
+    void *userptr;
+    char host[PING_MAX_HOSTLEN];
+
+    int fd;
+    unsigned int seq;
+    struct sockaddr_storage addr;
+    socklen_t addr_len;
+    struct fast_ping_packet packet;
+};
+
+struct fast_ping_struct {
+    int run;
+    pthread_t tid;
+    pthread_mutex_t lock;
+    int ident;
+
+    int epoll_fd;
+    int fd_icmp;
+    struct ping_host_struct icmp_host;
+    int fd_icmp6;
+    struct ping_host_struct icmp6_host;
+
+    pthread_mutex_t map_lock;
+    DECLARE_HASHTABLE(hostmap, 6);
+    DECLARE_HASHTABLE(addrmap, 6);
+};
+
+static struct fast_ping_struct ping;
+static fast_ping_result ping_callback;
+
+uint16_t _fast_ping_checksum(uint16_t *header, size_t len)
+{
+    uint32_t sum = 0;
+    int i;
+
+    for (i = 0; i < len / sizeof(uint16_t); i++) {
+        sum += ntohs(header[i]);
+    }
+
+    return htons(~((sum >> 16) + (sum & 0xffff)));
+}
+
+int fast_ping_result_callback(fast_ping_result result)
+{
+    ping_callback = result;
+}
+
+void _fast_ping_install_filter_v6(int sock)
+{
+    struct icmp6_filter icmp6_filter;
+    ICMP6_FILTER_SETBLOCKALL(&icmp6_filter);
+    ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &icmp6_filter);
+    setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &icmp6_filter, sizeof(struct icmp6_filter));
+
+    static int once;
+    static struct sock_filter insns[] = {
+        BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 4), /* Load icmp echo ident */
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA, 0, 1), /* Ours? */
+        BPF_STMT(BPF_RET | BPF_K, ~0U), /* Yes, it passes. */
+        BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 0), /* Load icmp type */
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ICMP6_ECHO_REPLY, 1, 0), /* Echo? */
+        BPF_STMT(BPF_RET | BPF_K, ~0U), /* No. It passes. This must not happen. */
+        BPF_STMT(BPF_RET | BPF_K, 0), /* Echo with wrong ident. Reject. */
+    };
+    static struct sock_fprog filter = { sizeof insns / sizeof(insns[0]), insns };
+
+    if (once) {
+        return;
+    }
+    once = 1;
+
+    /* Patch bpflet for current identifier. */
+    insns[1] = (struct sock_filter)BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(getpid()), 0, 1);
+
+    if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) {
+        perror("WARNING: failed to install socket filter\n");
+    }
+}
+
+void _fast_ping_install_filter_v4(int sock)
+{
+    static int once;
+    static struct sock_filter insns[] = {
+        BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0), /* Skip IP header. F..g BSD... Look into ping6. */
+        BPF_STMT(BPF_LD | BPF_H | BPF_IND, 4), /* Load icmp echo ident */
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA, 0, 1), /* Ours? */
+        BPF_STMT(BPF_RET | BPF_K, ~0U), /* Yes, it passes. */
+        BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), /* Load icmp type */
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ICMP_ECHOREPLY, 1, 0), /* Echo? */
+        BPF_STMT(BPF_RET | BPF_K, 0xFFFFFFF), /* No. It passes. */
+        BPF_STMT(BPF_RET | BPF_K, 0) /* Echo with wrong ident. Reject. */
+    };
+
+    static struct sock_fprog filter = { sizeof insns / sizeof(insns[0]), insns };
+
+    if (once) {
+        return;
+    }
+    once = 1;
+
+    /* Patch bpflet for current identifier. */
+    insns[2] = (struct sock_filter)BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(getpid()), 0, 1);
+
+    if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) {
+        perror("WARNING: failed to install socket filter\n");
+    }
+}
+
+static struct addrinfo *_fast_ping_getaddr(const u_char *host, int type, int protocol)
+{
+    struct addrinfo hints;
+    struct addrinfo *result = NULL;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = type;
+    hints.ai_protocol = protocol;
+    if (getaddrinfo(host, NULL, &hints, &result) != 0) {
+        fprintf(stderr, "get addr info failed. %s\n", strerror(errno));
+        goto errout;
+    }
+
+    return result;
+errout:
+    if (result) {
+        freeaddrinfo(result);
+    }
+    return NULL;
+}
+
+static int _fast_ping_getdomain(const u_char *host)
+{
+    struct addrinfo hints;
+    struct addrinfo *result = NULL;
+    int domain = -1;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_protocol = 0;
+    if (getaddrinfo(host, NULL, &hints, &result) != 0) {
+        fprintf(stderr, "get addr info failed. %s\n", strerror(errno));
+        goto errout;
+    }
+
+    domain = result->ai_family;
+
+    freeaddrinfo(result);
+
+    return domain;
+errout:
+    if (result) {
+        freeaddrinfo(result);
+    }
+    return -1;
+}
+
+int _fast_ping_host_get(struct ping_host_struct *ping_host)
+{
+    atomic_inc(&ping_host->ref);
+}
+
+int _fast_ping_host_put(struct ping_host_struct *ping_host)
+{
+    pthread_mutex_lock(&ping.map_lock);
+    if (atomic_dec_and_test(&ping_host->ref)) {
+        hlist_del(&ping_host->host_node);
+        hlist_del(&ping_host->addr_node);
+    } else {
+        ping_host = NULL;
+    }
+    pthread_mutex_unlock(&ping.map_lock);
+
+    if (ping_host == NULL) {
+        return -1;
+    }
+
+    free(ping_host);
+}
+
+static int _fast_ping_sendping_v6(struct ping_host_struct *ping_host)
+{
+    struct fast_ping_packet *packet = &ping_host->packet;
+    struct icmp6_hdr *icmp6 = &packet->icmp6;
+    int len = 0;
+
+    ping_host->seq++;
+    memset(icmp6, 0, sizeof(*icmp6));
+    icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
+    icmp6->icmp6_code = 0;
+    icmp6->icmp6_cksum = 0;
+    icmp6->icmp6_id = getpid();
+    icmp6->icmp6_seq = htons(ping_host->seq);
+
+    gettimeofday(&packet->msg.tv, 0);
+    icmp6->icmp6_cksum = _fast_ping_checksum((void *)packet, sizeof(struct fast_ping_packet));
+
+    len = sendto(ping_host->fd, &ping_host->packet, sizeof(struct fast_ping_packet), 0, (struct sockaddr *)&ping_host->addr, ping_host->addr_len);
+    if (len < 0 || len != sizeof(struct fast_ping_packet)) {
+        fprintf(stderr, "sendto %s\n", strerror(errno));
+        goto errout;
+    }
+
+    return 0;
+
+errout:
+    return -1;
+}
+
+static int _fast_ping_sendping_v4(struct ping_host_struct *ping_host)
+{
+    struct fast_ping_packet *packet = &ping_host->packet;
+    struct icmp *icmp = &packet->icmp;
+    int len;
+
+    ping_host->seq++;
+    memset(icmp, 0, sizeof(*icmp));
+    icmp->icmp_type = ICMP_ECHO;
+    icmp->icmp_code = 0;
+    icmp->icmp_cksum = 0;
+    icmp->icmp_id = ping.ident;
+    icmp->icmp_seq = htons(ping_host->seq);
+
+    gettimeofday(&packet->msg.tv, 0);
+    icmp->icmp_cksum = _fast_ping_checksum((void *)packet, sizeof(struct fast_ping_packet));
+
+    len = sendto(ping_host->fd, packet, sizeof(struct fast_ping_packet), 0, (struct sockaddr *)&ping_host->addr, ping_host->addr_len);
+    if (len < 0 || len != sizeof(struct fast_ping_packet)) {
+        fprintf(stderr, "sendto %s\n", strerror(errno));
+        goto errout;
+    }
+
+    return 0;
+
+errout:
+    return -1;
+}
+
+static int _fast_ping_sendping(struct ping_host_struct *ping_host)
+{
+    if (ping_host->type == AF_INET) {
+        return _fast_ping_sendping_v4(ping_host);
+    } else if (ping_host->type == AF_INET6) {
+        return _fast_ping_sendping_v6(ping_host);
+    }
+
+    return -1;
+}
+
+static int _fast_ping_create_sock(int protocol)
+{
+    int fd;
+    struct ping_host_struct *icmp_host = NULL;
+    struct epoll_event event;
+
+    fd = socket(AF_INET, SOCK_RAW, protocol);
+    if (fd < 0) {
+        fprintf(stderr, "create icmp socket failed.\n");
+        goto errout;
+    }
+    switch (protocol) {
+    case IPPROTO_ICMP:
+        _fast_ping_install_filter_v4(fd);
+        icmp_host = &ping.icmp_host;
+        break;
+    case IPPROTO_ICMPV6:
+        _fast_ping_install_filter_v6(fd);
+        icmp_host = &ping.icmp_host;
+        break;
+    }
+
+    event.events = EPOLLIN;
+    event.data.ptr = icmp_host;
+    if (epoll_ctl(ping.epoll_fd, EPOLL_CTL_ADD, fd, &event) != 0) {
+        goto errout;
+    }
+
+    icmp_host->fd = fd;
+    icmp_host->type = AF_PACKET;
+    return fd;
+
+errout:
+    close(fd);
+    return -1;
+}
+
+static int _fast_ping_create_icmp(int protocol)
+{
+    int fd = 0;
+    int *set_fd = NULL;
+
+    pthread_mutex_lock(&ping.lock);
+    switch (protocol) {
+    case IPPROTO_ICMP:
+        set_fd = &ping.fd_icmp;
+        break;
+    case IPPROTO_ICMPV6:
+        set_fd = &ping.fd_icmp6;
+        break;
+    default:
+        goto errout;
+        break;
+    }
+
+    if (*set_fd > 0) {
+        goto out;
+    }
+
+    fd = _fast_ping_create_sock(protocol);
+    if (fd < 0) {
+        goto errout;
+    }
+
+    *set_fd = fd;
+out:
+    pthread_mutex_unlock(&ping.lock);
+    return *set_fd;
+errout:
+    if (fd > 0) {
+        close(fd);
+    }
+    pthread_mutex_unlock(&ping.lock);
+    return -1;
+}
+
+int fast_ping_start(const char *host, int timeout, void *userptr)
+{
+    struct ping_host_struct *ping_host = NULL;
+    struct addrinfo *gai = NULL;
+    int domain = -1;
+    int icmp_proto = 0;
+    char ip[PING_MAX_HOSTLEN];
+    uint32_t hostkey;
+    uint32_t addrkey;
+
+    domain = _fast_ping_getdomain(host);
+    if (domain < 0) {
+        return -1;
+    }
+
+    switch (domain) {
+    case AF_INET:
+        icmp_proto = IPPROTO_ICMP;
+        break;
+    case AF_INET6:
+        icmp_proto = IPPROTO_ICMPV6;
+        break;
+    default:
+        return -1;
+        break;
+    }
+
+    gai = _fast_ping_getaddr(host, SOCK_RAW, icmp_proto);
+    if (gai == NULL) {
+        return -1;
+    }
+
+    ping_host = malloc(sizeof(*ping_host));
+    if (ping_host == NULL) {
+        goto errout;
+    }
+
+    memset(ping_host, 0, sizeof(ping_host));
+    strncpy(ping_host->host, host, PING_MAX_HOSTLEN);
+    ping_host->type = domain;
+    ping_host->fd = _fast_ping_create_icmp(icmp_proto);
+    memcpy(&ping_host->addr, gai->ai_addr, gai->ai_addrlen);
+    ping_host->addr_len = gai->ai_addrlen;
+
+    atomic_set(&ping_host->ref, 0);
+
+    hostkey = hash_string(ping_host->host);
+	addrkey = jhash(&ping_host->addr, ping_host->addr_len, 0);
+    pthread_mutex_lock(&ping.map_lock);
+    _fast_ping_host_get(ping_host);
+    hash_add(ping.hostmap, &ping_host->host_node, hostkey);
+    hash_add(ping.addrmap, &ping_host->addr_node, addrkey);
+    pthread_mutex_unlock(&ping.map_lock);
+
+    freeaddrinfo(gai);
+
+    _fast_ping_sendping(ping_host);
+    return 0;
+errout:
+    if (gai) {
+        freeaddrinfo(gai);
+    }
+
+    if (ping_host) {
+        free(ping_host);
+    }
+
+    return -1;
+}
+
+int fast_ping_stop(const char *host)
+{
+    struct ping_host_struct *ping_host;
+    uint32_t key;
+    key = hash_string(host);
+    pthread_mutex_lock(&ping.map_lock);
+    hash_for_each_possible(ping.hostmap, ping_host, host_node, key)
+    {
+        if (strncmp(host, ping_host->host, PING_MAX_HOSTLEN) == 0) {
+            break;
+        }
+    }
+    if (ping_host == NULL) {
+        pthread_mutex_unlock(&ping.map_lock);
+        return -1;
+    }
+    pthread_mutex_unlock(&ping.map_lock);
+    _fast_ping_host_put(ping_host);
+    return 0;
+}
+
+void tv_sub(struct timeval *out, struct timeval *in)
+{
+    if ((out->tv_usec -= in->tv_usec) < 0) { /* out -= in */
+        --out->tv_sec;
+        out->tv_usec += 1000000;
+    }
+    out->tv_sec -= in->tv_sec;
+}
+
+static int _fast_ping_icmp6_packet(struct ping_host_struct *ping_host, u_char *packet_data, int data_len, struct timeval *tvrecv)
+{
+    int hlen;
+    int icmp_len;
+    struct fast_ping_packet *packet = (struct fast_ping_packet *)packet_data;
+    struct icmp6_hdr *icmp6 = &packet->icmp6;
+    struct timeval tvresult = *tvrecv;
+    double rtt;
+
+    if (icmp6->icmp6_type != ICMP6_ECHO_REPLY) {
+        return -1;
+    }
+
+    icmp_len = data_len;
+    if (icmp_len < 16) {
+        return -1;
+    }
+
+    if (icmp6->icmp6_id != ping.ident) {
+        return -1;
+    }
+
+    struct timeval *tvsend = &packet->msg.tv;
+    tv_sub(&tvresult, tvsend);
+    ping_callback(ping_host->host, ping_host->seq, &tvresult, ping_host->userptr);
+
+    return 0;
+}
+
+static int _fast_ping_icmp_packet(struct ping_host_struct *ping_host, u_char *packet_data, int data_len, struct timeval *tvrecv)
+{
+    struct ip *ip = (struct ip *)packet_data;
+    struct fast_ping_packet *packet;
+    struct icmp *icmp;
+    struct timeval tvresult = *tvrecv;
+    int hlen;
+    int icmp_len;
+
+    if (ip->ip_p != IPPROTO_ICMP) {
+        return -1;
+    }
+
+    hlen = ip->ip_hl << 2;
+    packet = (struct fast_ping_packet *)(packet_data + hlen);
+    icmp = &packet->icmp;
+    icmp_len = data_len - hlen;
+
+    if (icmp_len < 16) {
+        return -1;
+    }
+
+    if (icmp->icmp_type != ICMP_ECHOREPLY) {
+        return -1;
+    }
+
+    if (icmp->icmp_id != ping.ident) {
+        return -1;
+    }
+
+    struct timeval *tvsend = &packet->msg.tv;
+    tv_sub(&tvresult, tvsend);
+
+    ping_callback(ping_host->host, ping_host->seq, &tvresult, ping_host->userptr);
+
+    return 0;
+}
+
+static int _fast_ping_recvping(struct ping_host_struct *ping_host, u_char *inpacket, int len, struct timeval *tvrecv)
+{
+
+    if (ping_host->type == AF_INET6) {
+        if (_fast_ping_icmp6_packet(ping_host, inpacket, len, tvrecv)) {
+            goto errout;
+        }
+    } else if (ping_host->type == AF_INET) {
+
+        if (_fast_ping_icmp_packet(ping_host, inpacket, len, tvrecv)) {
+            goto errout;
+        }
+    }
+
+    return 0;
+errout:
+    return -1;
+}
+
+static int _fast_ping_create_tcp(struct ping_host_struct *ping_host)
+{
+    return -1;
+}
+
+static int _fast_ping_ping_host(struct ping_host_struct *ping_host) {}
+
+static int _fast_ping_gethost_by_addr(u_char *host, struct sockaddr *addr, socklen_t addr_len)
+{
+    struct sockaddr_storage *addr_store = (struct sockaddr_storage *)addr;
+    host[0] = 0;
+    switch (addr_store->ss_family) {
+    case AF_INET: {
+        struct sockaddr_in *addr_in;
+        addr_in = (struct sockaddr_in *)addr;
+        inet_ntop(AF_INET, &addr_in->sin_addr, host, addr_len);
+    } break;
+    case AF_INET6: {
+        struct sockaddr_in6 *addr_in6;
+        addr_in6 = (struct sockaddr_in6 *)addr;
+        if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) {
+            struct sockaddr_in addr_in4;
+            memset(&addr_in4, 0, sizeof(addr_in4));
+            memcpy(&addr_in4.sin_addr.s_addr, addr_in6->sin6_addr.s6_addr + 12, sizeof(addr_in4.sin_addr.s_addr));
+        } else {
+            inet_ntop(AF_INET6, &addr_in6->sin6_addr, host, addr_len);
+        }
+    } break;
+    default:
+        goto errout;
+        break;
+    }
+    return 0;
+errout:
+    return -1;
+}
+
+static int _fast_ping_process(struct ping_host_struct *ping_host, struct timeval *now)
+{
+    int len;
+    u_char inpacket[ICMP_INPACKET_SIZE];
+    struct sockaddr_storage from;
+    struct ping_host_struct *recv_ping_host;
+    socklen_t from_len = sizeof(from);
+    uint32_t addrkey;
+
+    len = recvfrom(ping_host->fd, inpacket, sizeof(inpacket), 0, (struct sockaddr *)&from, (socklen_t *)&from_len);
+    if (len < 0) {
+        fprintf(stderr, "recvfrom failed, %s\n", strerror(errno));
+        goto errout;
+    }
+
+	addrkey = jhash(&from, from_len, 0);
+    pthread_mutex_lock(&ping.map_lock);
+    hash_for_each_possible(ping.addrmap, recv_ping_host, addr_node, addrkey)
+    {
+        if (recv_ping_host->addr_len == from_len && memcmp(&recv_ping_host->addr, &from, from_len) == 0) {
+            break;
+        }
+    }
+    pthread_mutex_unlock(&ping.map_lock);
+
+    if (recv_ping_host == NULL) {
+        return -1;
+    }
+
+    _fast_ping_recvping(recv_ping_host, inpacket, len, now);
+    return 0;
+errout:
+    return -1;
+}
+
+static void _fast_ping_period_run()
+{
+    struct ping_host_struct *ping_host;
+    struct hlist_node *tmp;
+    int i = 0;
+    pthread_mutex_lock(&ping.map_lock);
+    hash_for_each_safe(ping.addrmap, i, tmp, ping_host, addr_node)
+    {
+        _fast_ping_sendping(ping_host);
+    }
+    pthread_mutex_unlock(&ping.map_lock);
+}
+
+static void *_fast_ping_work(void *arg)
+{
+    struct epoll_event events[PING_MAX_EVENTS + 1];
+    int num;
+    int i;
+    struct timeval last = { 0 };
+    struct timeval now = { 0 };
+
+    while (ping.run) {
+        if (last.tv_sec != now.tv_sec) {
+            _fast_ping_period_run();
+            last = now;
+        }
+
+        num = epoll_wait(ping.epoll_fd, events, PING_MAX_EVENTS, 1000);
+        if (num < 0) {
+            gettimeofday(&now, 0);
+            usleep(100000);
+            continue;
+        }
+
+        if (num == 0) {
+            gettimeofday(&now, 0);
+            continue;
+        }
+
+        gettimeofday(&now, 0);
+        for (i = 0; i < num; i++) {
+            struct epoll_event *event = &events[i];
+            struct ping_host_struct *ping_host = (struct ping_host_struct *)event->data.ptr;
+            _fast_ping_process(ping_host, &now);
+        }
+    }
+
+    close(ping.epoll_fd);
+    ping.epoll_fd = -1;
+
+    return NULL;
+}
+
+int fast_ping_init()
+{
+    pthread_attr_t attr;
+    int epollfd = -1;
+    int ret;
+
+    if (ping.epoll_fd > 0) {
+        return -1;
+    }
+
+    memset(&ping, 0, sizeof(ping));
+    pthread_attr_init(&attr);
+
+    epollfd = epoll_create1(EPOLL_CLOEXEC);
+    if (epollfd < 0) {
+        fprintf(stderr, "create epoll failed, %s\n", strerror(errno));
+        goto errout;
+    }
+
+    ping.run = 1;
+    ret = pthread_create(&ping.tid, &attr, _fast_ping_work, NULL);
+    if (ret != 0) {
+        fprintf(stderr, "create ping work thread failed, %s\n", strerror(errno));
+        goto errout;
+    }
+
+    pthread_mutex_init(&ping.map_lock, 0);
+    pthread_mutex_init(&ping.lock, 0);
+    hash_init(ping.hostmap);
+    hash_init(ping.addrmap);
+    ping.epoll_fd = epollfd;
+    ping.ident = getpid();
+
+    return 0;
+errout:
+    if (ping.tid > 0) {
+        void *retval = NULL;
+        ping.run = 0;
+        pthread_join(ping.tid, &retval);
+    }
+
+    if (epollfd) {
+        close(epollfd);
+    }
+
+    pthread_mutex_destroy(&ping.lock);
+    pthread_mutex_destroy(&ping.map_lock);
+
+    return -1;
+}
+
+int fast_ping_exit()
+{
+    if (ping.tid > 0) {
+        void *ret = NULL;
+        ping.run = 0;
+        pthread_join(ping.tid, &ret);
+    }
+
+    if (ping.fd_icmp > 0) {
+        close(ping.fd_icmp);
+        ping.fd_icmp < 0;
+    }
+
+    if (ping.fd_icmp6 > 0) {
+        close(ping.fd_icmp6);
+        ping.fd_icmp6 < 0;
+    }
+
+    pthread_mutex_destroy(&ping.lock);
+    pthread_mutex_destroy(&ping.map_lock);
+}

+ 31 - 0
fast_ping.h

@@ -0,0 +1,31 @@
+#ifndef FAST_PING_H
+#define FAST_PING_H
+
+#include <sys/time.h>
+
+#ifdef __cpluscplus
+extern "C" {
+#endif
+
+typedef enum {
+	FAST_PING_ICMP = 1,
+	FAST_PING_TCP,
+	FAST_PING_UDP
+} FAST_PING_TYPE;
+
+typedef void (*fast_ping_result)(const char *host, int seqno, struct timeval *tv, void *userptr);
+int fast_ping_result_callback(fast_ping_result result);
+
+int fast_ping_start(const char *host, int timeout, void *userptr);
+
+int fast_ping_stop(const char *host);
+
+int fast_ping_init();
+
+int fast_ping_exit();
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif // !FAST_PING_H

+ 133 - 0
include/atomic.h

@@ -0,0 +1,133 @@
+//Source: http://golubenco.org/atomic-operations.html
+#ifndef _GENERIC_ATOMIC_H
+#define _GENERIC_ATOMIC_H
+
+/* Check GCC version, just to be safe */
+#if !defined(__GNUC__) || (__GNUC__ < 4) || (__GNUC_MINOR__ < 1)
+# error atomic.h works only with GCC newer than version 4.1
+#endif /* GNUC >= 4.1 */
+
+/**
+ * Atomic type.
+ */
+typedef struct {
+	volatile int counter;
+} atomic_t;
+
+#define ATOMIC_INIT(i)  { (i) }
+
+/**
+ * Read atomic variable
+ * @param v pointer of type atomic_t
+ *
+ * Atomically reads the value of @v.
+ */
+#define atomic_read(v) ((v)->counter)
+
+/**
+ * Set atomic variable
+ * @param v pointer of type atomic_t
+ * @param i required value
+ */
+#define atomic_set(v,i) (((v)->counter) = (i))
+
+/**
+ * Add to the atomic variable
+ * @param i integer value to add
+ * @param v pointer of type atomic_t
+ */
+static inline void atomic_add( int i, atomic_t *v )
+{
+	(void)__sync_add_and_fetch(&v->counter, i);
+}
+
+/**
+ * Subtract the atomic variable
+ * @param i integer value to subtract
+ * @param v pointer of type atomic_t
+ *
+ * Atomically subtracts @i from @v.
+ */
+static inline void atomic_sub( int i, atomic_t *v )
+{
+	(void)__sync_sub_and_fetch(&v->counter, i);
+}
+
+/**
+ * Subtract value from variable and test result
+ * @param i integer value to subtract
+ * @param v pointer of type atomic_t
+ *
+ * Atomically subtracts @i from @v and returns
+ * true if the result is zero, or false for all
+ * other cases.
+ */
+static inline int atomic_sub_and_test( int i, atomic_t *v )
+{
+	return !(__sync_sub_and_fetch(&v->counter, i));
+}
+
+/**
+ * Increment atomic variable
+ * @param v pointer of type atomic_t
+ *
+ * Atomically increments @v by 1.
+ */
+static inline void atomic_inc( atomic_t *v )
+{
+	(void)__sync_fetch_and_add(&v->counter, 1);
+}
+
+/**
+ * @brief decrement atomic variable
+ * @param v: pointer of type atomic_t
+ *
+ * Atomically decrements @v by 1.  Note that the guaranteed
+ * useful range of an atomic_t is only 24 bits.
+ */
+static inline void atomic_dec( atomic_t *v )
+{
+	(void)__sync_fetch_and_sub(&v->counter, 1);
+}
+
+/**
+ * @brief Decrement and test
+ * @param v pointer of type atomic_t
+ *
+ * Atomically decrements @v by 1 and
+ * returns true if the result is 0, or false for all other
+ * cases.
+ */
+static inline int atomic_dec_and_test( atomic_t *v )
+{
+	return !(__sync_sub_and_fetch(&v->counter, 1));
+}
+
+/**
+ * @brief Increment and test
+ * @param v pointer of type atomic_t
+ *
+ * Atomically increments @v by 1
+ * and returns true if the result is zero, or false for all
+ * other cases.
+ */
+static inline int atomic_inc_and_test( atomic_t *v )
+{
+	return !(__sync_add_and_fetch(&v->counter, 1));
+}
+
+/**
+ * @brief add and test if negative
+ * @param v pointer of type atomic_t
+ * @param i integer value to add
+ *
+ * Atomically adds @i to @v and returns true
+ * if the result is negative, or false when
+ * result is greater than or equal to zero.
+ */
+static inline int atomic_add_negative( int i, atomic_t *v )
+{
+	return (__sync_add_and_fetch(&v->counter, i) < 0);
+}
+
+#endif

+ 132 - 0
include/bitmap.h

@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _PERF_BITOPS_H
+#define _PERF_BITOPS_H
+
+#include <string.h>
+#include <stdlib.h>
+#include "bitops.h"
+
+#define DECLARE_BITMAP(name,bits) \
+	unsigned long name[BITS_TO_LONGS(bits)]
+
+int __bitmap_weight(const unsigned long *bitmap, int bits);
+void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
+		 const unsigned long *bitmap2, int bits);
+int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
+		 const unsigned long *bitmap2, unsigned int bits);
+
+#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1)))
+
+#define BITMAP_LAST_WORD_MASK(nbits)					\
+(									\
+	((nbits) % BITS_PER_LONG) ?					\
+		(1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL		\
+)
+
+#define small_const_nbits(nbits) \
+	(__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
+
+static inline void bitmap_zero(unsigned long *dst, int nbits)
+{
+	if (small_const_nbits(nbits))
+		*dst = 0UL;
+	else {
+		int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
+		memset(dst, 0, len);
+	}
+}
+
+static inline void bitmap_fill(unsigned long *dst, unsigned int nbits)
+{
+	unsigned int nlongs = BITS_TO_LONGS(nbits);
+	if (!small_const_nbits(nbits)) {
+		unsigned int len = (nlongs - 1) * sizeof(unsigned long);
+		memset(dst, 0xff,  len);
+	}
+	dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits);
+}
+
+static inline int bitmap_empty(const unsigned long *src, unsigned nbits)
+{
+	if (small_const_nbits(nbits))
+		return ! (*src & BITMAP_LAST_WORD_MASK(nbits));
+
+	return find_first_bit(src, nbits) == nbits;
+}
+
+static inline int bitmap_full(const unsigned long *src, unsigned int nbits)
+{
+	if (small_const_nbits(nbits))
+		return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits));
+
+	return find_first_zero_bit(src, nbits) == nbits;
+}
+
+static inline int bitmap_weight(const unsigned long *src, int nbits)
+{
+	if (small_const_nbits(nbits))
+		return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits));
+	return __bitmap_weight(src, nbits);
+}
+
+static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
+			     const unsigned long *src2, int nbits)
+{
+	if (small_const_nbits(nbits))
+		*dst = *src1 | *src2;
+	else
+		__bitmap_or(dst, src1, src2, nbits);
+}
+
+/**
+ * test_and_set_bit - Set a bit and return its old value
+ * @nr: Bit to set
+ * @addr: Address to count from
+ */
+static inline int test_and_set_bit(int nr, unsigned long *addr)
+{
+	unsigned long mask = BIT_MASK(nr);
+	unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+	unsigned long old;
+
+	old = *p;
+	*p = old | mask;
+
+	return (old & mask) != 0;
+}
+
+/**
+ * bitmap_alloc - Allocate bitmap
+ * @nbits: Number of bits
+ */
+static inline unsigned long *bitmap_alloc(int nbits)
+{
+	return calloc(1, BITS_TO_LONGS(nbits) * sizeof(unsigned long));
+}
+
+/*
+ * bitmap_scnprintf - print bitmap list into buffer
+ * @bitmap: bitmap
+ * @nbits: size of bitmap
+ * @buf: buffer to store output
+ * @size: size of @buf
+ */
+size_t bitmap_scnprintf(unsigned long *bitmap, int nbits,
+			char *buf, size_t size);
+
+/**
+ * bitmap_and - Do logical and on bitmaps
+ * @dst: resulting bitmap
+ * @src1: operand 1
+ * @src2: operand 2
+ * @nbits: size of bitmap
+ */
+static inline int bitmap_and(unsigned long *dst, const unsigned long *src1,
+			     const unsigned long *src2, unsigned int nbits)
+{
+	if (small_const_nbits(nbits))
+		return (*dst = *src1 & *src2 & BITMAP_LAST_WORD_MASK(nbits)) != 0;
+	return __bitmap_and(dst, src1, src2, nbits);
+}
+
+#endif /* _PERF_BITOPS_H */

+ 65 - 0
include/bitops.h

@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _GENERIC_BITOPS_H_
+#define _GENERIC_BITOPS_H_
+
+#ifndef __WORDSIZE
+#define __WORDSIZE (__SIZEOF_LONG__ * 8)
+#endif
+
+#ifndef BITS_PER_LONG
+# define BITS_PER_LONG __WORDSIZE
+#endif
+
+#define BIT_MASK(nr)		(1UL << ((nr) % BITS_PER_LONG))
+#define BIT_WORD(nr)		((nr) / BITS_PER_LONG)
+#define BITS_PER_BYTE		8
+#define BITS_TO_LONGS(nr)	DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
+#define BITS_TO_U64(nr)		DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64))
+#define BITS_TO_U32(nr)		DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))
+#define BITS_TO_BYTES(nr)	DIV_ROUND_UP(nr, BITS_PER_BYTE)
+
+extern unsigned int __sw_hweight8(unsigned int w);
+extern unsigned int __sw_hweight16(unsigned int w);
+extern unsigned int __sw_hweight32(unsigned int w);
+extern unsigned long __sw_hweight64(uint64_t w);
+
+
+#define for_each_set_bit(bit, addr, size) \
+	for ((bit) = find_first_bit((addr), (size));		\
+	     (bit) < (size);					\
+	     (bit) = find_next_bit((addr), (size), (bit) + 1))
+
+#define for_each_clear_bit(bit, addr, size) \
+	for ((bit) = find_first_zero_bit((addr), (size));       \
+	     (bit) < (size);                                    \
+	     (bit) = find_next_zero_bit((addr), (size), (bit) + 1))
+
+/* same as for_each_set_bit() but use bit as value to start with */
+#define for_each_set_bit_from(bit, addr, size) \
+	for ((bit) = find_next_bit((addr), (size), (bit));	\
+	     (bit) < (size);					\
+	     (bit) = find_next_bit((addr), (size), (bit) + 1))
+
+static inline unsigned long hweight_long(unsigned long w)
+{
+	return sizeof(w) == 4 ? hweight32(w) : hweight64(w);
+}
+
+static inline unsigned fls_long(unsigned long l)
+{
+	if (sizeof(l) == 4)
+		return fls(l);
+	return fls64(l);
+}
+
+/**
+ * rol32 - rotate a 32-bit value left
+ * @word: value to rotate
+ * @shift: bits to roll
+ */
+static inline uint32_t rol32(uint32_t word, unsigned int shift)
+{
+	return (word << shift) | (word >> ((-shift) & 31));
+}
+
+#endif

+ 201 - 0
include/hash.h

@@ -0,0 +1,201 @@
+#ifndef _GENERIC_HASH_H
+#define _GENERIC_HASH_H
+
+#include "jhash.h"
+
+/* Fast hashing routine for ints,  longs and pointers.
+   (C) 2002 Nadia Yvette Chambers, IBM */
+#ifndef __WORDSIZE
+#define __WORDSIZE (__SIZEOF_LONG__ * 8)
+#endif
+
+#ifndef BITS_PER_LONG
+# define BITS_PER_LONG __WORDSIZE
+#endif
+
+/*
+ * non-constant log of base 2 calculators
+ * - the arch may override these in asm/bitops.h if they can be implemented
+ *   more efficiently than using fls() and fls64()
+ * - the arch is not required to handle n==0 if implementing the fallback
+ */
+static inline __attribute__((const))
+int __ilog2_u32(uint32_t n)
+{
+	return fls(n) - 1;
+}
+
+static inline __attribute__((const))
+int __ilog2_u64(uint64_t n)
+{
+	return fls64(n) - 1;
+}
+
+/**
+ * ilog2 - log of base 2 of 32-bit or a 64-bit unsigned value
+ * @n - parameter
+ *
+ * constant-capable log of base 2 calculation
+ * - this can be used to initialise global variables from constant data, hence
+ *   the massive ternary operator construction
+ *
+ * selects the appropriately-sized optimised version depending on sizeof(n)
+ */
+#define ilog2(n)				\
+(						\
+	__builtin_constant_p(n) ? (		\
+		(n) < 2 ? 0 :			\
+		(n) & (1ULL << 63) ? 63 :	\
+		(n) & (1ULL << 62) ? 62 :	\
+		(n) & (1ULL << 61) ? 61 :	\
+		(n) & (1ULL << 60) ? 60 :	\
+		(n) & (1ULL << 59) ? 59 :	\
+		(n) & (1ULL << 58) ? 58 :	\
+		(n) & (1ULL << 57) ? 57 :	\
+		(n) & (1ULL << 56) ? 56 :	\
+		(n) & (1ULL << 55) ? 55 :	\
+		(n) & (1ULL << 54) ? 54 :	\
+		(n) & (1ULL << 53) ? 53 :	\
+		(n) & (1ULL << 52) ? 52 :	\
+		(n) & (1ULL << 51) ? 51 :	\
+		(n) & (1ULL << 50) ? 50 :	\
+		(n) & (1ULL << 49) ? 49 :	\
+		(n) & (1ULL << 48) ? 48 :	\
+		(n) & (1ULL << 47) ? 47 :	\
+		(n) & (1ULL << 46) ? 46 :	\
+		(n) & (1ULL << 45) ? 45 :	\
+		(n) & (1ULL << 44) ? 44 :	\
+		(n) & (1ULL << 43) ? 43 :	\
+		(n) & (1ULL << 42) ? 42 :	\
+		(n) & (1ULL << 41) ? 41 :	\
+		(n) & (1ULL << 40) ? 40 :	\
+		(n) & (1ULL << 39) ? 39 :	\
+		(n) & (1ULL << 38) ? 38 :	\
+		(n) & (1ULL << 37) ? 37 :	\
+		(n) & (1ULL << 36) ? 36 :	\
+		(n) & (1ULL << 35) ? 35 :	\
+		(n) & (1ULL << 34) ? 34 :	\
+		(n) & (1ULL << 33) ? 33 :	\
+		(n) & (1ULL << 32) ? 32 :	\
+		(n) & (1ULL << 31) ? 31 :	\
+		(n) & (1ULL << 30) ? 30 :	\
+		(n) & (1ULL << 29) ? 29 :	\
+		(n) & (1ULL << 28) ? 28 :	\
+		(n) & (1ULL << 27) ? 27 :	\
+		(n) & (1ULL << 26) ? 26 :	\
+		(n) & (1ULL << 25) ? 25 :	\
+		(n) & (1ULL << 24) ? 24 :	\
+		(n) & (1ULL << 23) ? 23 :	\
+		(n) & (1ULL << 22) ? 22 :	\
+		(n) & (1ULL << 21) ? 21 :	\
+		(n) & (1ULL << 20) ? 20 :	\
+		(n) & (1ULL << 19) ? 19 :	\
+		(n) & (1ULL << 18) ? 18 :	\
+		(n) & (1ULL << 17) ? 17 :	\
+		(n) & (1ULL << 16) ? 16 :	\
+		(n) & (1ULL << 15) ? 15 :	\
+		(n) & (1ULL << 14) ? 14 :	\
+		(n) & (1ULL << 13) ? 13 :	\
+		(n) & (1ULL << 12) ? 12 :	\
+		(n) & (1ULL << 11) ? 11 :	\
+		(n) & (1ULL << 10) ? 10 :	\
+		(n) & (1ULL <<  9) ?  9 :	\
+		(n) & (1ULL <<  8) ?  8 :	\
+		(n) & (1ULL <<  7) ?  7 :	\
+		(n) & (1ULL <<  6) ?  6 :	\
+		(n) & (1ULL <<  5) ?  5 :	\
+		(n) & (1ULL <<  4) ?  4 :	\
+		(n) & (1ULL <<  3) ?  3 :	\
+		(n) & (1ULL <<  2) ?  2 :	\
+		1 ) :				\
+	(sizeof(n) <= 4) ?			\
+	__ilog2_u32(n) :			\
+	__ilog2_u64(n)				\
+ )
+
+/*
+ * This hash multiplies the input by a large odd number and takes the
+ * high bits.  Since multiplication propagates changes to the most
+ * significant end only, it is essential that the high bits of the
+ * product be used for the hash value.
+ *
+ * Chuck Lever verified the effectiveness of this technique:
+ * http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf
+ *
+ * Although a random odd number will do, it turns out that the golden
+ * ratio phi = (sqrt(5)-1)/2, or its negative, has particularly nice
+ * properties.  (See Knuth vol 3, section 6.4, exercise 9.)
+ *
+ * These are the negative, (1 - phi) = phi**2 = (3 - sqrt(5))/2,
+ * which is very slightly easier to multiply by and makes no
+ * difference to the hash distribution.
+ */
+#define GOLDEN_RATIO_32 0x61C88647
+#define GOLDEN_RATIO_64 0x61C8864680B583EBull
+
+/*
+ * The _generic versions exist only so lib/test_hash.c can compare
+ * the arch-optimized versions with the generic.
+ *
+ * Note that if you change these, any <asm/hash.h> that aren't updated
+ * to match need to have their HAVE_ARCH_* define values updated so the
+ * self-test will not false-positive.
+ */
+#ifndef HAVE_ARCH__HASH_32
+#define __hash_32 __hash_32_generic
+#endif
+static inline uint32_t __hash_32_generic(uint32_t val)
+{
+	return val * GOLDEN_RATIO_32;
+}
+
+#ifndef HAVE_ARCH_HASH_32
+#define hash_32 hash_32_generic
+#endif
+static inline uint32_t hash_32_generic(uint32_t val, unsigned int bits)
+{
+	/* High bits are more random, so use them. */
+	return __hash_32(val) >> (32 - bits);
+}
+
+#ifndef HAVE_ARCH_HASH_64
+#define hash_64 hash_64_generic
+#endif
+static inline uint32_t hash_64(uint64_t val, unsigned int bits)
+{
+#if BITS_PER_LONG == 64
+	/* 64x64-bit multiply is efficient on all 64-bit processors */
+	return val * GOLDEN_RATIO_64 >> (64 - bits);
+#else
+	/* Hash 64 bits using only 32x32-bit multiply. */
+	return hash_32((uint32_t)val ^ __hash_32(val >> 32), bits);
+#endif
+}
+
+static inline uint32_t hash_ptr(const void *ptr, unsigned int bits)
+{
+	return hash_long((unsigned long)ptr, bits);
+}
+
+/* This really should be called fold32_ptr; it does no hashing to speak of. */
+static inline uint32_t hash32_ptr(const void *ptr)
+{
+	unsigned long val = (unsigned long)ptr;
+
+#if BITS_PER_LONG == 64
+	val ^= (val >> 32);
+#endif
+	return (uint32_t)val;
+}
+
+static inline unsigned long
+hash_string(const char *str)
+{
+	unsigned long v = 0;
+    const char *c;
+	for (c = str; *c; )
+		v = (((v << 1) + (v >> 14)) ^ (*c++)) & 0x3fff;
+	return(v);
+}
+
+#endif /* _GENERIC_HASH_H */

+ 153 - 0
include/hashtable.h

@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Statically sized hash table implementation
+ * (C) 2012  Sasha Levin <[email protected]>
+ */
+
+#ifndef _GENERIC_HASHTABLE_H
+#define _GENERIC_HASHTABLE_H
+
+#include "list.h"
+#include "hash.h"
+#include "bitmap.h"
+
+#ifndef __same_type
+# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
+#endif
+#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+#define __must_be_array(a)	BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
+
+#define DEFINE_HASHTABLE(name, bits)						\
+	struct hlist_head name[1 << (bits)] =					\
+			{ [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
+
+#define DECLARE_HASHTABLE(name, bits)                                   	\
+	struct hlist_head name[1 << (bits)]
+
+#define HASH_SIZE(name) (ARRAY_SIZE(name))
+#define HASH_BITS(name) ilog2(HASH_SIZE(name))
+
+/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */
+#define hash_min(val, bits)							\
+	(sizeof(val) <= 4 ? hash_32(val, bits) : hash_long(val, bits))
+
+static inline void __hash_init(struct hlist_head *ht, unsigned int sz)
+{
+	unsigned int i;
+
+	for (i = 0; i < sz; i++)
+		INIT_HLIST_HEAD(&ht[i]);
+}
+
+/**
+ * hash_init - initialize a hash table
+ * @hashtable: hashtable to be initialized
+ *
+ * Calculates the size of the hashtable from the given parameter, otherwise
+ * same as hash_init_size.
+ *
+ * This has to be a macro since HASH_BITS() will not work on pointers since
+ * it calculates the size during preprocessing.
+ */
+#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable))
+
+/**
+ * hash_add - add an object to a hashtable
+ * @hashtable: hashtable to add to
+ * @node: the &struct hlist_node of the object to be added
+ * @key: the key of the object to be added
+ */
+#define hash_add(hashtable, node, key)						\
+	hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))])
+
+/**
+ * hash_hashed - check whether an object is in any hashtable
+ * @node: the &struct hlist_node of the object to be checked
+ */
+static inline bool hash_hashed(struct hlist_node *node)
+{
+	return !hlist_unhashed(node);
+}
+
+static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz)
+{
+	unsigned int i;
+
+	for (i = 0; i < sz; i++)
+		if (!hlist_empty(&ht[i]))
+			return false;
+
+	return true;
+}
+
+/**
+ * hash_empty - check whether a hashtable is empty
+ * @hashtable: hashtable to check
+ *
+ * This has to be a macro since HASH_BITS() will not work on pointers since
+ * it calculates the size during preprocessing.
+ */
+#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable))
+
+/**
+ * hash_del - remove an object from a hashtable
+ * @node: &struct hlist_node of the object to remove
+ */
+static inline void hash_del(struct hlist_node *node)
+{
+	hlist_del_init(node);
+}
+
+/**
+ * hash_for_each - iterate over a hashtable
+ * @name: hashtable to iterate
+ * @bkt: integer to use as bucket loop cursor
+ * @obj: the type * to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ */
+#define hash_for_each(name, bkt, obj, member)				\
+	for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
+			(bkt)++)\
+		hlist_for_each_entry(obj, &name[bkt], member)
+
+/**
+ * hash_for_each_safe - iterate over a hashtable safe against removal of
+ * hash entry
+ * @name: hashtable to iterate
+ * @bkt: integer to use as bucket loop cursor
+ * @tmp: a &struct used for temporary storage
+ * @obj: the type * to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ */
+#define hash_for_each_safe(name, bkt, tmp, obj, member)			\
+	for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
+			(bkt)++)\
+		hlist_for_each_entry_safe(obj, tmp, &name[bkt], member)
+
+/**
+ * hash_for_each_possible - iterate over all possible objects hashing to the
+ * same bucket
+ * @name: hashtable to iterate
+ * @obj: the type * to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ * @key: the key of the objects to iterate over
+ */
+#define hash_for_each_possible(name, obj, member, key)			\
+	hlist_for_each_entry(obj, &name[hash_min(key, HASH_BITS(name))], member)
+
+/**
+ * hash_for_each_possible_safe - iterate over all possible objects hashing to the
+ * same bucket safe against removals
+ * @name: hashtable to iterate
+ * @obj: the type * to use as a loop cursor for each entry
+ * @tmp: a &struct used for temporary storage
+ * @member: the name of the hlist_node within the struct
+ * @key: the key of the objects to iterate over
+ */
+#define hash_for_each_possible_safe(name, obj, tmp, member, key)	\
+	hlist_for_each_entry_safe(obj, tmp,\
+		&name[hash_min(key, HASH_BITS(name))], member)
+
+
+#endif

+ 184 - 0
include/jhash.h

@@ -0,0 +1,184 @@
+#ifndef _JHASH_H
+#define _JHASH_H
+
+#include <stdint.h>
+
+/* jhash.h: Jenkins hash support.
+ *
+ * Copyright (C) 2006. Bob Jenkins ([email protected])
+ *
+ * http://burtleburtle.net/bob/hash/
+ *
+ * These are the credits from Bob's sources:
+ *
+ * lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+ *
+ * These are functions for producing 32-bit hashes for hash table lookup.
+ * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+ * are externally useful functions.  Routines to test the hash are included
+ * if SELF_TEST is defined.  You can use this free for any purpose.  It's in
+ * the public domain.  It has no warranty.
+ *
+ * Copyright (C) 2009-2010 Jozsef Kadlecsik ([email protected])
+ *
+ * I've modified Bob's hash to be useful in the Linux kernel, and
+ * any bugs present are my fault.
+ * Jozsef
+ */
+#include "bitops.h"
+
+/* Best hash sizes are of power of two */
+#define jhash_size(n)   ((uint32_t)1<<(n))
+/* Mask the hash value, i.e (value & jhash_mask(n)) instead of (value % n) */
+#define jhash_mask(n)   (jhash_size(n)-1)
+
+/* __jhash_mix -- mix 3 32-bit values reversibly. */
+#define __jhash_mix(a, b, c)			\
+{						\
+	a -= c;  a ^= rol32(c, 4);  c += b;	\
+	b -= a;  b ^= rol32(a, 6);  a += c;	\
+	c -= b;  c ^= rol32(b, 8);  b += a;	\
+	a -= c;  a ^= rol32(c, 16); c += b;	\
+	b -= a;  b ^= rol32(a, 19); a += c;	\
+	c -= b;  c ^= rol32(b, 4);  b += a;	\
+}
+
+/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */
+#define __jhash_final(a, b, c)			\
+{						\
+	c ^= b; c -= rol32(b, 14);		\
+	a ^= c; a -= rol32(c, 11);		\
+	b ^= a; b -= rol32(a, 25);		\
+	c ^= b; c -= rol32(b, 16);		\
+	a ^= c; a -= rol32(c, 4);		\
+	b ^= a; b -= rol32(a, 14);		\
+	c ^= b; c -= rol32(b, 24);		\
+}
+
+/* An arbitrary initial parameter */
+#define JHASH_INITVAL		0xdeadbeef
+
+struct __una_u32 { uint32_t x; } __packed;
+
+static inline uint32_t __get_unaligned_cpu32(const void *p)
+{
+	const struct __una_u32 *ptr = (const struct __una_u32 *)p;
+	return ptr->x;
+}
+
+/* jhash - hash an arbitrary key
+ * @k: sequence of bytes as key
+ * @length: the length of the key
+ * @initval: the previous hash, or an arbitray value
+ *
+ * The generic version, hashes an arbitrary sequence of bytes.
+ * No alignment or length assumptions are made about the input key.
+ *
+ * Returns the hash value of the key. The result depends on endianness.
+ */
+static inline uint32_t jhash(const void *key, uint32_t length, uint32_t initval)
+{
+	uint32_t a, b, c;
+	const uint8_t *k = key;
+
+	/* Set up the internal state */
+	a = b = c = JHASH_INITVAL + length + initval;
+
+	/* All but the last block: affect some 32 bits of (a,b,c) */
+	while (length > 12) {
+		a += __get_unaligned_cpu32(k);
+		b += __get_unaligned_cpu32(k + 4);
+		c += __get_unaligned_cpu32(k + 8);
+		__jhash_mix(a, b, c);
+		length -= 12;
+		k += 12;
+	}
+	/* Last block: affect all 32 bits of (c) */
+	/* All the case statements fall through */
+	switch (length) {
+	case 12: c += (uint32_t)k[11]<<24;
+	case 11: c += (uint32_t)k[10]<<16;
+	case 10: c += (uint32_t)k[9]<<8;
+	case 9:  c += k[8];
+	case 8:  b += (uint32_t)k[7]<<24;
+	case 7:  b += (uint32_t)k[6]<<16;
+	case 6:  b += (uint32_t)k[5]<<8;
+	case 5:  b += k[4];
+	case 4:  a += (uint32_t)k[3]<<24;
+	case 3:  a += (uint32_t)k[2]<<16;
+	case 2:  a += (uint32_t)k[1]<<8;
+	case 1:  a += k[0];
+		 __jhash_final(a, b, c);
+	case 0: /* Nothing left to add */
+		break;
+	}
+
+	return c;
+}
+
+/* jhash2 - hash an array of uint32_t's
+ * @k: the key which must be an array of uint32_t's
+ * @length: the number of uint32_t's in the key
+ * @initval: the previous hash, or an arbitray value
+ *
+ * Returns the hash value of the key.
+ */
+static inline uint32_t jhash2(const uint32_t *k, uint32_t length, uint32_t initval)
+{
+	uint32_t a, b, c;
+
+	/* Set up the internal state */
+	a = b = c = JHASH_INITVAL + (length<<2) + initval;
+
+	/* Handle most of the key */
+	while (length > 3) {
+		a += k[0];
+		b += k[1];
+		c += k[2];
+		__jhash_mix(a, b, c);
+		length -= 3;
+		k += 3;
+	}
+
+	/* Handle the last 3 uint32_t's: all the case statements fall through */
+	switch (length) {
+	case 3: c += k[2];
+	case 2: b += k[1];
+	case 1: a += k[0];
+		__jhash_final(a, b, c);
+	case 0:	/* Nothing left to add */
+		break;
+	}
+
+	return c;
+}
+
+
+/* __jhash_nwords - hash exactly 3, 2 or 1 word(s) */
+static inline uint32_t __jhash_nwords(uint32_t a, uint32_t b, uint32_t c, uint32_t initval)
+{
+	a += initval;
+	b += initval;
+	c += initval;
+
+	__jhash_final(a, b, c);
+
+	return c;
+}
+
+static inline uint32_t jhash_3words(uint32_t a, uint32_t b, uint32_t c, uint32_t initval)
+{
+	return __jhash_nwords(a, b, c, initval + JHASH_INITVAL + (3 << 2));
+}
+
+static inline uint32_t jhash_2words(uint32_t a, uint32_t b, uint32_t initval)
+{
+	return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2));
+}
+
+static inline uint32_t jhash_1word(uint32_t a, uint32_t initval)
+{
+	return __jhash_nwords(a, 0, 0, initval + JHASH_INITVAL + (1 << 2));
+}
+
+#endif /* _JHASH_H */

+ 792 - 0
include/list.h

@@ -0,0 +1,792 @@
+
+#ifndef _GENERIC_LIST_H
+#define _GENERIC_LIST_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#ifndef container_of
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr:	the pointer to the member.
+ * @type:	the type of the container struct this is embedded in.
+ * @member:	the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({			\
+	const typeof(((type *)0)->member) * __mptr = (ptr);	\
+	(type *)((char *)__mptr - offsetof(type, member)); })
+#endif
+
+#define LIST_POISON1  ((void *) 0x100)
+#define LIST_POISON2  ((void *) 0x200)
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+struct hlist_head {
+	struct hlist_node *first;
+};
+
+struct hlist_node {
+	struct hlist_node *next, **pprev;
+};
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+			      struct list_head *prev,
+			      struct list_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void __list_del_entry(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+}
+
+static inline void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->next = LIST_POISON1;
+	entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+				struct list_head *new)
+{
+	new->next = old->next;
+	new->next->prev = new;
+	new->prev = old->prev;
+	new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+					struct list_head *new)
+{
+	list_replace(old, new);
+	INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+	__list_del_entry(entry);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+	__list_del_entry(list);
+	list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+				  struct list_head *head)
+{
+	__list_del_entry(list);
+	list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+				const struct list_head *head)
+{
+	return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+	struct list_head *next = head->next;
+	return (next == head) && (next == head->prev);
+}
+
+/**
+ * list_rotate_left - rotate the list to the left
+ * @head: the head of the list
+ */
+static inline void list_rotate_left(struct list_head *head)
+{
+	struct list_head *first;
+
+	if (!list_empty(head)) {
+		first = head->next;
+		list_move_tail(first, head);
+	}
+}
+
+/**
+ * list_is_singular - tests whether a list has just one entry.
+ * @head: the list to test.
+ */
+static inline int list_is_singular(const struct list_head *head)
+{
+	return !list_empty(head) && (head->next == head->prev);
+}
+
+static inline void __list_cut_position(struct list_head *list,
+		struct list_head *head, struct list_head *entry)
+{
+	struct list_head *new_first = entry->next;
+	list->next = head->next;
+	list->next->prev = list;
+	list->prev = entry;
+	entry->next = list;
+	head->next = new_first;
+	new_first->prev = head;
+}
+
+/**
+ * list_cut_position - cut a list into two
+ * @list: a new list to add all removed entries
+ * @head: a list with entries
+ * @entry: an entry within head, could be the head itself
+ *	and if so we won't cut the list
+ *
+ * This helper moves the initial part of @head, up to and
+ * including @entry, from @head to @list. You should
+ * pass on @entry an element you know is on @head. @list
+ * should be an empty list or a list you do not care about
+ * losing its data.
+ *
+ */
+static inline void list_cut_position(struct list_head *list,
+		struct list_head *head, struct list_head *entry)
+{
+	if (list_empty(head))
+		return;
+	if (list_is_singular(head) &&
+		(head->next != entry && head != entry))
+		return;
+	if (entry == head)
+		INIT_LIST_HEAD(list);
+	else
+		__list_cut_position(list, head, entry);
+}
+
+static inline void __list_splice(const struct list_head *list,
+				 struct list_head *prev,
+				 struct list_head *next)
+{
+	struct list_head *first = list->next;
+	struct list_head *last = list->prev;
+
+	first->prev = prev;
+	prev->next = first;
+
+	last->next = next;
+	next->prev = last;
+}
+
+/**
+ * list_splice - join two lists, this is designed for stacks
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(const struct list_head *list,
+				struct list_head *head)
+{
+	if (!list_empty(list))
+		__list_splice(list, head, head->next);
+}
+
+/**
+ * list_splice_tail - join two lists, each list being a queue
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice_tail(struct list_head *list,
+				struct list_head *head)
+{
+	if (!list_empty(list))
+		__list_splice(list, head->prev, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+				    struct list_head *head)
+{
+	if (!list_empty(list)) {
+		__list_splice(list, head, head->next);
+		INIT_LIST_HEAD(list);
+	}
+}
+
+/**
+ * list_splice_tail_init - join two lists and reinitialise the emptied list
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * Each of the lists is a queue.
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_tail_init(struct list_head *list,
+					 struct list_head *head)
+{
+	if (!list_empty(list)) {
+		__list_splice(list, head->prev, head);
+		INIT_LIST_HEAD(list);
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_head within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr:	the list head to take the element from.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_head within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+	list_entry((ptr)->next, type, member)
+
+/**
+ * list_last_entry - get the last element from a list
+ * @ptr:	the list head to take the element from.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_head within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_last_entry(ptr, type, member) \
+	list_entry((ptr)->prev, type, member)
+
+/**
+ * list_first_entry_or_null - get the first element from a list
+ * @ptr:	the list head to take the element from.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_head within the struct.
+ *
+ * Note that if the list is empty, it returns NULL.
+ */
+#define list_first_entry_or_null(ptr, type, member) \
+	(!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL)
+
+/**
+ * list_next_entry - get the next element in list
+ * @pos:	the type * to cursor
+ * @member:	the name of the list_head within the struct.
+ */
+#define list_next_entry(pos, member) \
+	list_entry((pos)->member.next, typeof(*(pos)), member)
+
+/**
+ * list_prev_entry - get the prev element in list
+ * @pos:	the type * to cursor
+ * @member:	the name of the list_head within the struct.
+ */
+#define list_prev_entry(pos, member) \
+	list_entry((pos)->member.prev, typeof(*(pos)), member)
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev	-	iterate over a list backwards
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+	for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev_safe(pos, n, head) \
+	for (pos = (head)->prev, n = pos->prev; \
+	     pos != (head); \
+	     pos = n, n = pos->prev)
+
+/**
+ * list_for_each_entry	-	iterate over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_head within the struct.
+ */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_first_entry(head, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = list_next_entry(pos, member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_head within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)			\
+	for (pos = list_last_entry(head, typeof(*pos), member);		\
+	     &pos->member != (head); 					\
+	     pos = list_prev_entry(pos, member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos:	the type * to use as a start point
+ * @head:	the head of the list
+ * @member:	the name of the list_head within the struct.
+ *
+ * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+	((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_head within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member) 		\
+	for (pos = list_next_entry(pos, member);			\
+	     &pos->member != (head);					\
+	     pos = list_next_entry(pos, member))
+
+/**
+ * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_head within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse(pos, head, member)		\
+	for (pos = list_prev_entry(pos, member);			\
+	     &pos->member != (head);					\
+	     pos = list_prev_entry(pos, member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_head within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member) 			\
+	for (; &pos->member != (head);					\
+	     pos = list_next_entry(pos, member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_head within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_first_entry(head, typeof(*pos), member),	\
+		n = list_next_entry(pos, member);			\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_next_entry(n, member))
+
+/**
+ * list_for_each_entry_safe_continue - continue list iteration safe against removal
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_head within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member) 		\
+	for (pos = list_next_entry(pos, member), 				\
+		n = list_next_entry(pos, member);				\
+	     &pos->member != (head);						\
+	     pos = n, n = list_next_entry(n, member))
+
+/**
+ * list_for_each_entry_safe_from - iterate over list from current point safe against removal
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_head within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member) 			\
+	for (n = list_next_entry(pos, member);					\
+	     &pos->member != (head);						\
+	     pos = n, n = list_next_entry(n, member))
+
+/**
+ * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_head within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member)		\
+	for (pos = list_last_entry(head, typeof(*pos), member),		\
+		n = list_prev_entry(pos, member);			\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_prev_entry(n, member))
+
+/**
+ * list_safe_reset_next - reset a stale list_for_each_entry_safe loop
+ * @pos:	the loop cursor used in the list_for_each_entry_safe loop
+ * @n:		temporary storage used in list_for_each_entry_safe
+ * @member:	the name of the list_head within the struct.
+ *
+ * list_safe_reset_next is not safe to use in general if the list may be
+ * modified concurrently (eg. the lock is dropped in the loop body). An
+ * exception to this is if the cursor element (pos) is pinned in the list,
+ * and list_safe_reset_next is called after re-taking the lock and before
+ * completing the current iteration of the loop body.
+ */
+#define list_safe_reset_next(pos, n, member)				\
+	n = list_next_entry(pos, member)
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+static inline void INIT_HLIST_NODE(struct hlist_node *h)
+{
+	h->next = NULL;
+	h->pprev = NULL;
+}
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+	return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+	return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+	struct hlist_node *next = n->next;
+	struct hlist_node **pprev = n->pprev;
+
+	*pprev = next;
+	if (next)
+		next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+	__hlist_del(n);
+	n->next = LIST_POISON1;
+	n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+	if (!hlist_unhashed(n)) {
+		__hlist_del(n);
+		INIT_HLIST_NODE(n);
+	}
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+	struct hlist_node *first = h->first;
+	n->next = first;
+	if (first)
+		first->pprev = &n->next;
+	h->first = n;
+	n->pprev = &h->first;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+					struct hlist_node *next)
+{
+	n->pprev = next->pprev;
+	n->next = next;
+	next->pprev = &n->next;
+	*(n->pprev) = n;
+}
+
+static inline void hlist_add_behind(struct hlist_node *n,
+				    struct hlist_node *prev)
+{
+	n->next = prev->next;
+	prev->next = n;
+	n->pprev = &prev->next;
+
+	if (n->next)
+		n->next->pprev  = &n->next;
+}
+
+/* after that we'll appear to be on some hlist and hlist_del will work */
+static inline void hlist_add_fake(struct hlist_node *n)
+{
+	n->pprev = &n->next;
+}
+
+static inline bool hlist_fake(struct hlist_node *h)
+{
+	return h->pprev == &h->next;
+}
+
+/*
+ * Move a list from one list head to another. Fixup the pprev
+ * reference of the first entry if it exists.
+ */
+static inline void hlist_move_list(struct hlist_head *old,
+				   struct hlist_head *new)
+{
+	new->first = old->first;
+	if (new->first)
+		new->first->pprev = &new->first;
+	old->first = NULL;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+	for (pos = (head)->first; pos ; pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+	for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+	     pos = n)
+
+#define hlist_entry_safe(ptr, type, member) \
+	({ typeof(ptr) ____ptr = (ptr); \
+	   ____ptr ? hlist_entry(____ptr, type, member) : NULL; \
+	})
+
+/**
+ * hlist_for_each_entry	- iterate over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(pos, head, member)				\
+	for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
+	     pos;							\
+	     pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after current point
+ * @pos:	the type * to use as a loop cursor.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(pos, member)			\
+	for (pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member);\
+	     pos;							\
+	     pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from current point
+ * @pos:	the type * to use as a loop cursor.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(pos, member)				\
+	for (; pos;							\
+	     pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another &struct hlist_node to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(pos, n, head, member) 		\
+	for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
+	     pos && ({ n = pos->member.next; 1; });			\
+	     pos = hlist_entry_safe(n, typeof(*pos), member))
+
+/**
+ * list_del_range - deletes range of entries from list.
+ * @begin: first element in the range to delete from the list.
+ * @end: last element in the range to delete from the list.
+ * Note: list_empty on the range of entries does not return true after this,
+ * the entries is in an undefined state.
+ */
+static inline void list_del_range(struct list_head *begin,
+				  struct list_head *end)
+{
+	begin->prev->next = end->next;
+	end->next->prev = begin->prev;
+}
+
+/**
+ * list_for_each_from	-	iterate over a list from one of its nodes
+ * @pos:  the &struct list_head to use as a loop cursor, from where to start
+ * @head: the head for your list.
+ */
+#define list_for_each_from(pos, head) \
+	for (; pos != (head); pos = pos->next)
+
+#endif /* _GENERIC_LIST_H */

+ 189 - 0
smartdns.c

@@ -0,0 +1,189 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "fast_ping.h"
+#include "list.h"
+#include "bitmap.h"
+#include "hashtable.h"
+
+void smartdns_ping_result(const char *host, int seqno, struct timeval *tv, void *userptr)
+{
+	double rtt = tv->tv_sec * 1000.0 + tv->tv_usec / 1000.0;
+	printf("%16s: seq=%d time=%.3f\n", host, seqno, rtt);
+}
+
+int smartdns_init()
+{
+	int ret;
+
+	ret = fast_ping_init();
+	if (ret != 0) {
+		fprintf(stderr, "start ping failed.\n");
+		goto errout;
+	}
+
+	fast_ping_result_callback(smartdns_ping_result);
+
+	return 0;
+errout:
+
+	return -1;
+}
+
+int smartdns_run()
+{
+	sleep(1);
+	fast_ping_start("192.168.1.1", 2000, 0);
+	fast_ping_start("192.168.1.35", 2000, 0);
+	fast_ping_start("14.215.177.38", 2000, 0);
+	fast_ping_start("113.96.161.87", 2000, 0);
+	//fast_ping_start("::1", 2000, 0);
+	while (1) {
+		sleep(10);
+		//fast_ping_stop("192.168.1.35");
+	}
+}
+
+void smartdns_exit()
+{
+	fast_ping_exit();
+}
+
+struct data {
+	struct list_head list;
+	int n;
+};
+
+void list_test()
+{
+	struct list_head head ;
+	struct list_head *iter;
+	int i = 0;
+
+	INIT_LIST_HEAD(&head);
+
+	for (i = 0; i < 10; i++) {
+		struct data *h = malloc(sizeof(struct data));
+		h->n = i;
+		list_add(&h->list, &head);
+	}
+
+	list_for_each(iter, &head) {
+        struct data *d= list_entry(iter, struct data, list);
+        printf("%d\n", d->n);
+    }
+}
+
+struct data_hash {
+	struct hlist_node node;
+	int n;
+	char str[32];
+};
+
+int hash_test()
+{
+	DEFINE_HASHTABLE(ht, 7);
+	struct data_hash *temp;
+	struct data_hash *obj;
+	int i;
+	int j;
+	int key;
+
+	for (i = 11; i < 17; i++) {
+		temp = malloc(sizeof(struct data_hash));
+		temp->n = i * i;
+		hash_add(ht, &temp->node, temp->n);
+	}
+
+	for (i = 11; i < 17; i++) {
+		key = i * i;
+		hash_for_each_possible(ht, obj, node, key) {
+			printf("value: %d\n", obj->n);
+		};
+	}
+
+    return 0;
+}
+
+int hash_string_test()
+{
+	DEFINE_HASHTABLE(ht, 7);
+	struct data_hash *temp;
+	struct data_hash *obj;
+	int i;
+	int j;
+	int key;
+
+	for (i = 0; i < 10; i++) {
+		temp = malloc(sizeof(struct data_hash));
+		sprintf(temp->str, "%d", i);
+		hash_add(ht, &temp->node, hash_string(temp->str));
+	}
+
+	for (i = 0; i < 10; i++) {
+		char key_str[32];
+		sprintf(key_str, "%d", i);
+		key = hash_string(key_str);
+		hash_for_each_possible(ht, obj, node, key) {
+			printf("i = %d value: %s\n", i, obj->str);
+		};
+	}
+
+    return 0;
+}
+
+#if 0
+struct data_rbtree {
+	struct rb_node list;
+	int value;
+};
+
+int rbtree_test()
+{
+	struct rb_root root;
+	struct rb_node *n;
+	RB_EMPTY_ROOT(&root);
+	int i;
+
+	for (i = 0; i < 10; i++)
+	{
+		struct data_rbtree *r = malloc(sizeof(struct data_rbtree));
+		r->value = i;
+		rb_insert(&r->list, &root);
+	}
+
+	n = rb_first(&root);
+	int num = 5;
+	while (n) {
+		struct data_rbtree *r = container_of(n, struct data_rbtree, list);
+		if (r->value < num) {
+			n = n->rb_left;
+		} else if (r->value > num) {
+			n = n->rb_right;
+		} else {
+			printf("n = %d\n", r->value);
+			break;
+		}
+	}
+
+	return 0;
+}
+#endif
+
+int main(int argc, char *argv[])
+{
+	int ret;
+
+	atexit(smartdns_exit);
+
+	ret = smartdns_init();
+	if (ret != 0) {
+		fprintf(stderr, "init smartdns failed.\n");
+		goto errout;
+	}
+
+	return smartdns_run();
+
+errout:
+
+	return 1;
+}

+ 755 - 0
test-dns.c

@@ -0,0 +1,755 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <ifaddrs.h>
+#include <string.h>
+#include <malloc.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#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 = 9000;
+
+  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);
+  }
+}