From c6af293927f19ef9fd04b6ff9357f53589b79996 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Sun, 22 Apr 2018 23:14:26 +0800 Subject: [PATCH] initial commit --- .clang-format | 9 + .gitignore | 4 + Makefile | 15 + ReadMe.md | 19 ++ fast_ping.c | 750 +++++++++++++++++++++++++++++++++++++++++ fast_ping.h | 31 ++ include/atomic.h | 133 ++++++++ include/bitmap.h | 132 ++++++++ include/bitops.h | 65 ++++ include/hash.h | 201 +++++++++++ include/hashtable.h | 153 +++++++++ include/jhash.h | 184 ++++++++++ include/list.h | 792 ++++++++++++++++++++++++++++++++++++++++++++ smartdns.c | 189 +++++++++++ test-dns.c | 755 +++++++++++++++++++++++++++++++++++++++++ 15 files changed, 3432 insertions(+) create mode 100755 .clang-format create mode 100644 .gitignore create mode 100755 Makefile create mode 100755 ReadMe.md create mode 100755 fast_ping.c create mode 100755 fast_ping.h create mode 100755 include/atomic.h create mode 100644 include/bitmap.h create mode 100644 include/bitops.h create mode 100644 include/hash.h create mode 100644 include/hashtable.h create mode 100644 include/jhash.h create mode 100644 include/list.h create mode 100755 smartdns.c create mode 100755 test-dns.c diff --git a/.clang-format b/.clang-format new file mode 100755 index 0000000..5924a68 --- /dev/null +++ b/.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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d9e7a1f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vscode +.o +.DS_Store +.swp. diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..f1a1bb0 --- /dev/null +++ b/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) \ No newline at end of file diff --git a/ReadMe.md b/ReadMe.md new file mode 100755 index 0000000..8be6c7c --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,19 @@ +Smart DNS +============== +Smard DNS会根据上级DNS服务返回的IP地址进行检查,如果IP地址无效,则继续进行DNS请求。 + + +特性 +-------------- +1. 多级DNS配置 + + +使用 +============== + + +License +=============== +GPL V2 License + + diff --git a/fast_ping.c b/fast_ping.c new file mode 100755 index 0000000..73b39aa --- /dev/null +++ b/fast_ping.c @@ -0,0 +1,750 @@ +#include "fast_ping.h" +#include "atomic.h" +#include "hashtable.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} \ No newline at end of file diff --git a/fast_ping.h b/fast_ping.h new file mode 100755 index 0000000..9220d7d --- /dev/null +++ b/fast_ping.h @@ -0,0 +1,31 @@ +#ifndef FAST_PING_H +#define FAST_PING_H + +#include + +#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 diff --git a/include/atomic.h b/include/atomic.h new file mode 100755 index 0000000..c82f5d3 --- /dev/null +++ b/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 diff --git a/include/bitmap.h b/include/bitmap.h new file mode 100644 index 0000000..8170981 --- /dev/null +++ b/include/bitmap.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PERF_BITOPS_H +#define _PERF_BITOPS_H + +#include +#include +#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 */ \ No newline at end of file diff --git a/include/bitops.h b/include/bitops.h new file mode 100644 index 0000000..8fb2e93 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/include/hash.h b/include/hash.h new file mode 100644 index 0000000..6d0b4ac --- /dev/null +++ b/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 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 */ \ No newline at end of file diff --git a/include/hashtable.h b/include/hashtable.h new file mode 100644 index 0000000..068070f --- /dev/null +++ b/include/hashtable.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Statically sized hash table implementation + * (C) 2012 Sasha Levin + */ + +#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 \ No newline at end of file diff --git a/include/jhash.h b/include/jhash.h new file mode 100644 index 0000000..bd03005 --- /dev/null +++ b/include/jhash.h @@ -0,0 +1,184 @@ +#ifndef _JHASH_H +#define _JHASH_H + +#include + +/* jhash.h: Jenkins hash support. + * + * Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net) + * + * 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 (kadlec@blackhole.kfki.hu) + * + * 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 */ \ No newline at end of file diff --git a/include/list.h b/include/list.h new file mode 100644 index 0000000..4aa9a43 --- /dev/null +++ b/include/list.h @@ -0,0 +1,792 @@ + +#ifndef _GENERIC_LIST_H +#define _GENERIC_LIST_H + +#include +#include +#include + +#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 */ \ No newline at end of file diff --git a/smartdns.c b/smartdns.c new file mode 100755 index 0000000..404acc8 --- /dev/null +++ b/smartdns.c @@ -0,0 +1,189 @@ +#include +#include +#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; +} \ No newline at end of file diff --git a/test-dns.c b/test-dns.c new file mode 100755 index 0000000..2fbf486 --- /dev/null +++ b/test-dns.c @@ -0,0 +1,755 @@ +#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 = 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); + } +}