Add systemd script

This commit is contained in:
Nick Peng
2018-06-16 02:36:04 +08:00
parent 53df9f7063
commit 7b62739c13
34 changed files with 746 additions and 869 deletions

18
src/Makefile Normal file
View File

@@ -0,0 +1,18 @@
BIN=smartdns
OBJS=smartdns.o fast_ping.o lib/bitops.o dns_client.o dns_server.o dns.o util.o tlog.o conf.o
CFLAGS=-g -O0 -Wall -Wstrict-prototypes -fno-omit-frame-pointer -Wstrict-aliasing
CFLAGS +=-Iinclude
CFLAGS += -DBASE_FILE_NAME=\"$(notdir $<)\"
CXXFLAGS=-g -O0 -Wall -std=c++11
CXXFLAGS +=-Iinclude
.PHONY: all
all: $(BIN)
$(BIN) : $(OBJS)
$(CC) $(OBJS) -o $@ -lpthread
clean:
$(RM) $(OBJS) $(BIN)

172
src/conf.c Normal file
View File

@@ -0,0 +1,172 @@
#include "conf.h"
#include "tlog.h"
#include "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_LINE_LEN 1024
#define MAX_KEY_LEN 64
#define DEFAULT_DNS_CACHE_SIZE 512
char dns_conf_server_ip[DNS_MAX_IPLEN];
int dns_conf_cachesize = DEFAULT_DNS_CACHE_SIZE;
struct dns_servers dns_conf_servers[DNS_MAX_SERVERS];
int dns_conf_server_num;
int dns_conf_loglevel = TLOG_ERROR;
int config_bind(char *value)
{
/* server bind address */
strncpy(dns_conf_server_ip, value, DNS_MAX_IPLEN);
return 0;
}
int config_server(char *value, dns_conf_server_type_t type)
{
int index = dns_conf_server_num;
struct dns_servers *server;
int port = -1;
if (index >= DNS_MAX_SERVERS) {
tlog(TLOG_ERROR, "exceeds max server number");
return -1;
}
server = &dns_conf_servers[index];
/* parse ip, port from value */
if (parse_ip(value, server->server, &port) != 0) {
return -1;
}
/* if port is not defined, set port to default 53 */
if (port == PORT_NOT_DEFINED) {
port= DEFAULT_DNS_PORT;
}
server->type = type;
server->port = port;
dns_conf_server_num++;
return 0;
}
int config_server_udp(char *value)
{
return config_server(value, DNS_CONF_TYPE_UDP);
}
int config_server_tcp(char *value)
{
return config_server(value, DNS_CONF_TYPE_TCP);
}
int config_server_http(char *value)
{
return config_server(value, DNS_CONF_TYPE_HTTP);
}
int config_cache_size(char *value)
{
/* read dns cache size */
int cache_size = atoi(value);
if (cache_size < 0) {
return -1;
}
dns_conf_cachesize = cache_size;
return 0;
}
int config_log_level(char *value)
{
/* read log level and set */
if (strncmp("debug", value, MAX_LINE_LEN) == 0) {
dns_conf_loglevel = TLOG_DEBUG;
} else if (strncmp("info", value, MAX_LINE_LEN) == 0) {
dns_conf_loglevel = TLOG_INFO;
} else if (strncmp("warn", value, MAX_LINE_LEN) == 0) {
dns_conf_loglevel = TLOG_WARN;
} else if (strncmp("error", value, MAX_LINE_LEN) == 0) {
dns_conf_loglevel = TLOG_ERROR;
}
return 0;
}
struct config_item {
const char *item;
int (*item_func)(char *value);
};
struct config_item config_item[] = {
{"bind", config_bind},
{"server", config_server_udp},
{"server-tcp", config_server_tcp},
{"server-http", config_server_http},
{"cache-size", config_cache_size},
{"loglevel", config_log_level},
};
int config_item_num = sizeof(config_item) / sizeof(struct config_item);
int load_conf(const char *file)
{
FILE *fp = NULL;
char line[MAX_LINE_LEN];
char key[MAX_KEY_LEN];
char value[MAX_LINE_LEN];
int filed_num = 0;
int line_num = 0;
int i;
fp = fopen(file, "r");
if (fp == NULL) {
tlog(TLOG_ERROR, "config file %s not exist.", file);
return -1;
}
while (fgets(line, MAX_LINE_LEN, fp)) {
line_num++;
filed_num = sscanf(line, "%63s %1023[^\r\n]s", key, value);
if (filed_num <= 0) {
continue;
}
/* comment, skip */
if (key[0] == '#') {
continue;
}
/* if field format is not key = value, error */
if (filed_num != 2) {
goto errout;
}
for (i = 0; i < config_item_num; i++) {
if (strncmp(config_item[i].item, key, MAX_KEY_LEN) != 0) {
continue;
}
/* call item function */
if (config_item[i].item_func(value) != 0) {
goto errout;
}
break;
}
}
fclose(fp);
return 0;
errout:
printf("invalid config at line %d: %s", line_num, line);
if (fp) {
fclose(fp);
}
return -1;
}

31
src/conf.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef _DNS_CONF
#define DNS_MAX_SERVERS 32
#define DNS_MAX_IPLEN 64
#define DNS_MAX_PATH 1024
#define DEFAULT_DNS_PORT 53
typedef enum dns_conf_server_type {
DNS_CONF_TYPE_UDP,
DNS_CONF_TYPE_TCP,
DNS_CONF_TYPE_HTTP,
} dns_conf_server_type_t;
struct dns_servers {
char server[DNS_MAX_IPLEN];
unsigned short port;
dns_conf_server_type_t type;
};
extern char dns_conf_server_ip[DNS_MAX_IPLEN];
extern int dns_conf_cachesize;
extern struct dns_servers dns_conf_servers[DNS_MAX_SERVERS];
extern int dns_conf_server_num;
extern int dns_conf_verbose;
extern int dns_conf_loglevel;
extern char dns_conf_logfile[DNS_MAX_PATH];
extern int dns_conf_lognum;
int load_conf(const char *file);
#endif // !_DNS_CONF

1471
src/dns.c Normal file

File diff suppressed because it is too large Load Diff

167
src/dns.h Normal file
View File

@@ -0,0 +1,167 @@
#ifndef _DNS_HEAD_H
#define _DNS_HEAD_H
#include <arpa/inet.h>
#include <linux/filter.h>
#include <netdb.h>
#include <stdint.h>
#define DNS_RR_A_LEN 4
#define DNS_RR_AAAA_LEN 16
#define DNS_MAX_CNAME_LEN 256
#define DNS_IN_PACKSIZE (512 * 4)
#define DNS_PACKSIZE (512 * 8)
typedef enum dns_qr {
DNS_QR_QUERY = 0,
DNS_QR_ANSWER = 1,
} dns_qr;
typedef enum dns_rr_type {
DNS_RRS_QD = 0,
DNS_RRS_AN = 1,
DNS_RRS_NS = 2,
DNS_RRS_NR = 3,
DNS_RRS_END = 4,
} dns_rr_type;
typedef enum dns_class { DNS_C_IN = 1, DNS_C_ANY = 255 } dns_class_t;
typedef enum dns_type {
DNS_T_A = 1,
DNS_T_NS = 2,
DNS_T_CNAME = 5,
DNS_T_SOA = 6,
DNS_T_PTR = 12,
DNS_T_MX = 15,
DNS_T_TXT = 16,
DNS_T_AAAA = 28,
DNS_T_SRV = 33,
DNS_T_OPT = 41,
DNS_T_SSHFP = 44,
DNS_T_SPF = 99,
DNS_T_AXFR = 252,
DNS_T_ALL = 255
} dns_type_t;
typedef enum dns_opcode {
DNS_OP_QUERY = 0,
DNS_OP_IQUERY = 1,
DNS_OP_STATUS = 2,
DNS_OP_NOTIFY = 4,
DNS_OP_UPDATE = 5,
} dns_opcode_t; /* dns_opcode */
typedef enum dns_rtcode {
DNS_RC_NOERROR = 0,
DNS_RC_FORMERR = 1,
DNS_RC_SERVFAIL = 2,
DNS_RC_NXDOMAIN = 3,
DNS_RC_NOTIMP = 4,
DNS_RC_REFUSED = 5,
DNS_RC_YXDOMAIN = 6,
DNS_RC_YXRRSET = 7,
DNS_RC_NXRRSET = 8,
DNS_RC_NOTAUTH = 9,
DNS_RC_NOTZONE = 10,
/* EDNS(0) extended RCODEs */
DNS_RC_BADVERS = 16,
} dns_rtcode_t; /* dns_rcode */
/* dns packet head */
struct dns_head {
unsigned short id; /* identification number */
unsigned short qr; /* Query/Response Flag */
unsigned short opcode; /* Operation Code */
unsigned char aa; /* Authoritative Answer Flag */
unsigned char tc; /* Truncation Flag */
unsigned char rd; /* Recursion Desired */
unsigned char ra; /* Recursion Available */
unsigned short rcode; /* Response Code */
unsigned short qdcount; /* number of question entries */
unsigned short ancount; /* number of answer entries */
unsigned short nscount; /* number of authority entries */
unsigned short nrcount; /* number of addititional resource entries */
} __attribute__((packed));
struct dns_rrs {
unsigned short next;
unsigned short len;
dns_type_t type;
unsigned char data[0];
};
/* packet haed */
struct dns_packet {
struct dns_head head;
unsigned short questions;
unsigned short answers;
unsigned short nameservers;
unsigned short additional;
int size;
int len;
unsigned char data[0];
};
/* RRS encode/decode context */
struct dns_data_context {
unsigned char *data;
unsigned char *ptr;
unsigned int maxsize;
};
/* packet encode/decode context */
struct dns_context {
struct dns_packet *packet;
unsigned char *data;
unsigned int maxsize;
unsigned char *ptr;
};
/* SOA data */
struct dns_soa {
char mname[DNS_MAX_CNAME_LEN];
char rname[DNS_MAX_CNAME_LEN];
unsigned int serial;
unsigned int refresh;
unsigned int retry;
unsigned int expire;
unsigned int minimum;
} __attribute__((packed));
;
struct dns_rrs *dns_get_rrs_next(struct dns_packet *packet, struct dns_rrs *rrs);
struct dns_rrs *dns_get_rrs_start(struct dns_packet *packet, dns_rr_type type, int *count);
/*
* Question
*/
int dns_add_domain(struct dns_packet *packet, char *domain, int qtype, int qclass);
int dns_get_domain(struct dns_rrs *rrs, char *domain, int maxsize, int *qtype, int *qclass);
/*
* Answers
*/
int dns_add_CNAME(struct dns_packet *packet, dns_rr_type type, char *domain, int ttl, char *cname);
int dns_get_CNAME(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, char *cname, int cname_size);
int dns_add_A(struct dns_packet *packet, dns_rr_type type, char *domain, int ttl, unsigned char addr[DNS_RR_A_LEN]);
int dns_get_A(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, unsigned char addr[DNS_RR_A_LEN]);
int dns_add_PTR(struct dns_packet *packet, dns_rr_type type, char *domain, int ttl, char *cname);
int dns_get_PTR(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, char *cname, int cname_size);
int dns_add_AAAA(struct dns_packet *packet, dns_rr_type type, char *domain, int ttl, unsigned char addr[DNS_RR_AAAA_LEN]);
int dns_get_AAAA(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, unsigned char addr[DNS_RR_AAAA_LEN]);
int dns_add_SOA(struct dns_packet *packet, dns_rr_type type, char *domain, int ttl, struct dns_soa *soa);
int dns_get_SOA(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, struct dns_soa *soa);
/*
* Packet operation
*/
int dns_decode(struct dns_packet *packet, int maxsize, unsigned char *data, int size);
int dns_encode(unsigned char *data, int size, struct dns_packet *packet);
int dns_packet_init(struct dns_packet *packet, int size, struct dns_head *head);
#endif

934
src/dns_client.c Normal file
View File

@@ -0,0 +1,934 @@
/*************************************************************************
*
* Copyright (C) 2018 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dns_client.h"
#include "atomic.h"
#include "dns.h"
#include "fast_ping.h"
#include "hashtable.h"
#include "list.h"
#include "tlog.h"
#include "util.h"
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.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/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define DNS_MAX_HOSTNAME 256
#define DNS_MAX_EVENTS 64
#define DNS_HOSTNAME_LEN 128
/* dns client */
struct dns_client {
pthread_t tid;
int run;
int epoll_fd;
/* dns server list */
pthread_mutex_t server_list_lock;
struct list_head dns_server_list;
/* query list */
pthread_mutex_t dns_request_lock;
struct list_head dns_request_list;
/* query doman hash table, key: sid + domain */
pthread_mutex_t domain_map_lock;
DECLARE_HASHTABLE(domain_map, 6);
/* client socket */
int udp;
};
/* dns server information */
struct dns_server_info {
struct list_head list;
/* server ping handle */
struct ping_host_struct *ping_host;
/* server type */
dns_server_type_t type;
/* server addr info */
unsigned short ss_family;
socklen_t addr_len;
union {
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr addr;
};
};
/* dns replied server info */
struct dns_query_replied {
struct hlist_node node;
socklen_t addr_len;
union {
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr addr;
};
};
/* query struct */
struct dns_query_struct {
atomic_t refcnt;
/* query id, hash key sid + domain*/
char domain[DNS_MAX_CNAME_LEN];
unsigned short sid;
struct hlist_node domain_node;
struct list_head dns_request_list;
struct list_head period_list;
/* dns query type */
int qtype;
/* dns query number */
atomic_t dns_request_sent;
unsigned long send_tick;
/* caller notification */
dns_client_callback callback;
void *user_ptr;
/* replied hash table */
DECLARE_HASHTABLE(replied_map, 4);
};
static struct dns_client client;
static atomic_t dns_client_sid = ATOMIC_INIT(0);
/* get addr info */
static struct addrinfo *_dns_client_getaddr(const char *host, char *port, int type, int protocol)
{
struct addrinfo hints;
struct addrinfo *result = NULL;
int ret = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = type;
hints.ai_protocol = protocol;
ret = getaddrinfo(host, port, &hints, &result);
if (ret != 0) {
tlog(TLOG_ERROR, "get addr info failed. %s\n", gai_strerror(errno));
tlog(TLOG_ERROR, "host = %s, port = %s, type = %d, protocol = %d", host, port, type, protocol);
goto errout;
}
return result;
errout:
if (result) {
freeaddrinfo(result);
}
return NULL;
}
/* check whether server exists */
int _dns_client_server_exist(struct addrinfo *gai, dns_server_type_t server_type)
{
struct dns_server_info *server_info, *tmp;
pthread_mutex_lock(&client.server_list_lock);
list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list)
{
if (server_info->addr_len != gai->ai_addrlen || server_info->ss_family != gai->ai_family) {
continue;
}
if (server_info->type != server_type) {
continue;
}
if (memcmp(&server_info->addr, gai->ai_addr, gai->ai_addrlen) != 0) {
continue;
}
pthread_mutex_lock(&client.server_list_lock);
return 0;
}
pthread_mutex_unlock(&client.server_list_lock);
return -1;
}
/* add dns server information */
int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type)
{
struct dns_server_info *server_info = NULL;
if (_dns_client_server_exist(gai, server_type) == 0) {
goto errout;
}
server_info = malloc(sizeof(*server_info));
if (server_info == NULL) {
goto errout;
}
memset(server_info, 0, sizeof(*server_info));
server_info->ss_family = gai->ai_family;
server_info->addr_len = gai->ai_addrlen;
server_info->type = server_type;
if (gai->ai_addrlen > sizeof(server_info->in6)) {
tlog(TLOG_ERROR, "addr len invalid, %d, %zd, %d", gai->ai_addrlen, sizeof(server_info->addr), server_info->ss_family);
goto errout;
}
memcpy(&server_info->addr, gai->ai_addr, gai->ai_addrlen);
/* start ping task */
server_info->ping_host = fast_ping_start(server_ip, 0, 60000, 1000, NULL, server_info);
if (server_info->ping_host == NULL) {
goto errout;
}
/* add to list */
pthread_mutex_lock(&client.server_list_lock);
list_add(&server_info->list, &client.dns_server_list);
pthread_mutex_unlock(&client.server_list_lock);
return 0;
errout:
if (server_info) {
if (server_info->ping_host) {
fast_ping_stop(server_info->ping_host);
}
free(server_info);
}
return -1;
}
/* remove all servers information */
void _dns_client_server_remove_all(void)
{
struct dns_server_info *server_info, *tmp;
pthread_mutex_lock(&client.server_list_lock);
list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list)
{
list_del(&server_info->list);
/* stop ping task */
if (fast_ping_stop(server_info->ping_host) != 0) {
tlog(TLOG_ERROR, "stop ping failed.\n");
}
free(server_info);
}
pthread_mutex_unlock(&client.server_list_lock);
}
/* remove single server */
int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type)
{
struct dns_server_info *server_info, *tmp;
/* find server and remove */
pthread_mutex_lock(&client.server_list_lock);
list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list)
{
if (server_info->addr_len != gai->ai_addrlen || server_info->ss_family != gai->ai_family) {
continue;
}
if (memcmp(&server_info->addr, gai->ai_addr, gai->ai_addrlen) != 0) {
continue;
}
list_del(&server_info->list);
pthread_mutex_unlock(&client.server_list_lock);
if (fast_ping_stop(server_info->ping_host) != 0) {
tlog(TLOG_ERROR, "stop ping failed.\n");
}
free(server_info);
return 0;
}
pthread_mutex_unlock(&client.server_list_lock);
return -1;
}
int _dns_client_server_operate(char *server_ip, int port, dns_server_type_t server_type, int operate)
{
char port_s[8];
int sock_type;
int ret;
struct addrinfo *gai = NULL;
if (server_type >= DNS_SERVER_TYPE_END) {
return -1;
}
if (server_type == DNS_SERVER_UDP) {
sock_type = SOCK_DGRAM;
} else {
sock_type = SOCK_STREAM;
}
/* get addr info */
snprintf(port_s, 8, "%d", port);
gai = _dns_client_getaddr(server_ip, port_s, sock_type, 0);
if (gai == NULL) {
tlog(TLOG_ERROR, "get address failed, %s:%d", server_ip, port);
goto errout;
}
if (operate == 0) {
ret = _dns_client_server_add(server_ip, gai, server_type);
if (ret != 0) {
goto errout;
}
} else {
ret = _dns_client_server_remove(server_ip, gai, server_type);
if (ret != 0) {
goto errout;
}
}
freeaddrinfo(gai);
return 0;
errout:
if (gai) {
freeaddrinfo(gai);
}
return -1;
}
int dns_add_server(char *server_ip, int port, dns_server_type_t server_type)
{
return _dns_client_server_operate(server_ip, port, server_type, 0);
}
int dns_remove_server(char *server_ip, int port, dns_server_type_t server_type)
{
return _dns_client_server_operate(server_ip, port, server_type, 1);
}
void _dns_client_query_release(struct dns_query_struct *query)
{
int refcnt = atomic_dec_return(&query->refcnt);
int bucket = 0;
struct dns_query_replied *replied_map;
struct hlist_node *tmp;
if (refcnt) {
if (refcnt < 0) {
tlog(TLOG_ERROR, "BUG: refcnt is %d", refcnt);
abort();
}
return;
}
/* notify caller query end */
if (query->callback) {
query->callback(query->domain, DNS_QUERY_END, NULL, NULL, 0, query->user_ptr);
}
/* free resource */
pthread_mutex_lock(&client.domain_map_lock);
list_del_init(&query->dns_request_list);
hash_del(&query->domain_node);
pthread_mutex_unlock(&client.domain_map_lock);
hash_for_each_safe(query->replied_map, bucket, tmp, replied_map, node)
{
hash_del(&replied_map->node);
free(replied_map);
}
memset(query, 0, sizeof(*query));
free(query);
}
void _dns_client_query_remove(struct dns_query_struct *query)
{
/* remove query from period check list, and release reference*/
pthread_mutex_lock(&client.domain_map_lock);
list_del_init(&query->dns_request_list);
hash_del(&query->domain_node);
pthread_mutex_unlock(&client.domain_map_lock);
_dns_client_query_release(query);
}
void _dns_client_query_remove_all(void)
{
struct dns_query_struct *query, *tmp;
LIST_HEAD(check_list);
pthread_mutex_lock(&client.domain_map_lock);
list_for_each_entry_safe(query, tmp, &client.dns_request_list, dns_request_list)
{
list_add(&query->period_list, &check_list);
}
pthread_mutex_unlock(&client.domain_map_lock);
list_for_each_entry_safe(query, tmp, &check_list, period_list)
{
list_del_init(&query->period_list);
_dns_client_query_remove(query);
}
return;
}
void _dns_client_query_get(struct dns_query_struct *query)
{
atomic_inc(&query->refcnt);
}
void _dns_client_period_run(void)
{
struct dns_query_struct *query, *tmp;
LIST_HEAD(check_list);
unsigned long now = get_tick_count();
/* get query which timed out to check list */
pthread_mutex_lock(&client.domain_map_lock);
list_for_each_entry_safe(query, tmp, &client.dns_request_list, dns_request_list)
{
if (now - query->send_tick >= 1000) {
list_add(&query->period_list, &check_list);
_dns_client_query_get(query);
}
}
pthread_mutex_unlock(&client.domain_map_lock);
list_for_each_entry_safe(query, tmp, &check_list, period_list)
{
/* free timed out query, and notify caller */
list_del_init(&query->period_list);
_dns_client_query_remove(query);
_dns_client_query_release(query);
}
return;
}
static struct dns_query_struct *_dns_client_get_request(unsigned short sid, char *domain)
{
struct dns_query_struct *query = NULL;
struct dns_query_struct *query_result = NULL;
struct hlist_node *tmp = NULL;
unsigned int key;
/* get query by hash key : id + domain */
key = hash_string(domain);
key = jhash(&sid, sizeof(sid), key);
pthread_mutex_lock(&client.domain_map_lock);
hash_for_each_possible_safe(client.domain_map, query, tmp, domain_node, key)
{
if (strncmp(query->domain, domain, DNS_MAX_CNAME_LEN) != 0) {
continue;
}
if (sid != query->sid) {
continue;
}
query_result = query;
break;
}
pthread_mutex_unlock(&client.domain_map_lock);
return query_result;
}
int _dns_replied_check_add(struct dns_query_struct *dns_query, struct sockaddr *addr, socklen_t addr_len)
{
int key = 0;
struct dns_query_replied *replied_map = NULL;
if (addr_len > sizeof(struct sockaddr_in6)) {
tlog(TLOG_ERROR, "addr length is invalid.");
return -1;
}
/* avoid multiple replies from one server */
key = jhash(addr, addr_len, 0);
hash_for_each_possible(dns_query->replied_map, replied_map, node, key)
{
/* already replied, ignore this reply */
if (memcmp(&replied_map->addr, addr, addr_len) == 0) {
return -1;
}
}
replied_map = malloc(sizeof(*replied_map));
if (replied_map == NULL) {
tlog(TLOG_ERROR, "malloc failed");
return -1;
}
/* add address info to check hashtable */
memcpy(&replied_map->addr, addr, addr_len);
hash_add(dns_query->replied_map, &replied_map->node, key);
return 0;
}
static int _dns_client_recv(unsigned char *inpacket, int inpacket_len, struct sockaddr_storage *from, socklen_t from_len)
{
int len;
int i;
int qtype;
int qclass;
char domain[DNS_MAX_CNAME_LEN];
int rr_count;
struct dns_rrs *rrs = NULL;
unsigned char packet_buff[DNS_PACKSIZE];
struct dns_packet *packet = (struct dns_packet *)packet_buff;
int ret = 0;
struct dns_query_struct *query;
int request_num = 0;
packet->head.tc = 0;
/* decode domain from udp packet */
len = dns_decode(packet, DNS_PACKSIZE, inpacket, inpacket_len);
if (len != 0) {
tlog(TLOG_ERROR, "decode failed, packet len = %d, tc=%d, %d\n", inpacket_len, packet->head.tc, packet->head.id);
return -1;
}
/* not answer, return error */
if (packet->head.qr != DNS_OP_IQUERY) {
tlog(TLOG_ERROR, "message type error.\n");
return -1;
}
tlog(TLOG_DEBUG, "qdcount = %d, ancount = %d, nscount = %d, nrcount = %d, len = %d, id = %d, tc = %d, rd = %d, ra = %d, rcode = %d\n", packet->head.qdcount,
packet->head.ancount, packet->head.nscount, packet->head.nrcount, inpacket_len, packet->head.id, packet->head.tc, packet->head.rd, packet->head.ra,
packet->head.rcode);
/* get question */
rrs = dns_get_rrs_start(packet, DNS_RRS_QD, &rr_count);
for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) {
dns_get_domain(rrs, domain, DNS_MAX_CNAME_LEN, &qtype, &qclass);
tlog(TLOG_DEBUG, "domain: %s qtype: %d qclass: %d\n", domain, qtype, qclass);
}
/* get query reference */
query = _dns_client_get_request(packet->head.id, domain);
if (query == NULL) {
return 0;
}
/* avoid multiple replies */
if (_dns_replied_check_add(query, (struct sockaddr *)from, from_len) != 0) {
return 0;
}
request_num = atomic_dec_return(&query->dns_request_sent);
if (request_num < 0) {
tlog(TLOG_ERROR, "send count is invalid, %d", request_num);
return -1;
}
/* notify caller dns query result */
if (query->callback) {
ret = query->callback(query->domain, DNS_QUERY_RESULT, packet, inpacket, inpacket_len, query->user_ptr);
}
if (request_num == 0 || ret) {
/* if all server replied, or done, stop query, release resource */
_dns_client_query_remove(query);
}
return ret;
}
static int _dns_client_process(struct dns_query_struct *dns_query, unsigned long now)
{
int len;
unsigned char inpacket[DNS_IN_PACKSIZE];
struct sockaddr_storage from;
socklen_t from_len = sizeof(from);
char from_host[DNS_MAX_CNAME_LEN];
/* receive from udp */
len = recvfrom(client.udp, inpacket, sizeof(inpacket), 0, (struct sockaddr *)&from, (socklen_t *)&from_len);
if (len < 0) {
tlog(TLOG_ERROR, "recvfrom failed, %s\n", strerror(errno));
return -1;
}
tlog(TLOG_DEBUG, "recv from %s", gethost_by_addr(from_host, (struct sockaddr *)&from, from_len));
if (_dns_client_recv(inpacket, len, &from, from_len) != 0) {
return -1;
}
return 0;
}
static void *_dns_client_work(void *arg)
{
struct epoll_event events[DNS_MAX_EVENTS + 1];
int num;
int i;
unsigned long now = {0};
unsigned int sleep = 100;
int sleep_time;
unsigned int expect_time = 0;
sleep_time = sleep;
now = get_tick_count() - sleep;
expect_time = now + sleep;
while (client.run) {
now = get_tick_count();
if (now >= expect_time) {
_dns_client_period_run();
sleep_time = sleep - (now - expect_time);
if (sleep_time < 0) {
sleep_time = 0;
expect_time = now;
}
expect_time += sleep;
}
num = epoll_wait(client.epoll_fd, events, DNS_MAX_EVENTS, sleep_time);
if (num < 0) {
usleep(100000);
continue;
}
for (i = 0; i < num; i++) {
struct epoll_event *event = &events[i];
struct dns_query_struct *dns_query = (struct dns_query_struct *)event->data.ptr;
_dns_client_process(dns_query, now);
}
}
close(client.epoll_fd);
client.epoll_fd = -1;
return NULL;
}
static int _dns_client_send_udp(struct dns_server_info *server_info, void *packet, int len)
{
int send_len = 0;
send_len = sendto(client.udp, packet, len, 0, (struct sockaddr *)&server_info->addr, server_info->addr_len);
if (send_len != len) {
return -1;
}
return 0;
}
static int _dns_client_send_tcp(struct dns_server_info *server_info, void *packet, int len)
{
return -1;
}
static int _dns_client_send_packet(struct dns_query_struct *query, void *packet, int len)
{
struct dns_server_info *server_info, *tmp;
int ret = 0;
query->send_tick = get_tick_count();
/* send query to all dns servers */
pthread_mutex_lock(&client.server_list_lock);
list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list)
{
atomic_inc(&query->dns_request_sent);
switch (server_info->type) {
case DNS_SERVER_UDP:
/* udp query */
ret = _dns_client_send_udp(server_info, packet, len);
break;
case DNS_SERVER_TCP:
/* tcp query */
ret = _dns_client_send_tcp(server_info, packet, len);
break;
default:
/* unsupport query type */
ret = -1;
break;
}
if (ret != 0) {
char server_addr[128];
tlog(TLOG_ERROR, "send query to %s failed, %s", gethost_by_addr(server_addr, &server_info->addr, server_info->addr_len), strerror(errno));
atomic_dec(&query->dns_request_sent);
continue;
}
}
pthread_mutex_unlock(&client.server_list_lock);
return 0;
}
static int _dns_client_send_query(struct dns_query_struct *query, char *doamin)
{
unsigned char packet_buff[DNS_PACKSIZE];
unsigned char inpacket[DNS_IN_PACKSIZE];
struct dns_packet *packet = (struct dns_packet *)packet_buff;
int encode_len;
/* init dns packet head */
struct dns_head head;
memset(&head, 0, sizeof(head));
head.id = query->sid;
head.qr = DNS_QR_QUERY;
head.opcode = DNS_OP_QUERY;
head.aa = 0;
head.rd = 1;
head.ra = 0;
head.rcode = 0;
dns_packet_init(packet, DNS_PACKSIZE, &head);
/* add question */
dns_add_domain(packet, doamin, query->qtype, DNS_C_IN);
/* encode packet */
encode_len = dns_encode(inpacket, DNS_IN_PACKSIZE, packet);
if (encode_len <= 0) {
tlog(TLOG_ERROR, "encode query failed.");
return -1;
}
/* send query packet */
return _dns_client_send_packet(query, inpacket, encode_len);
}
int dns_client_query(char *domain, int qtype, dns_client_callback callback, void *user_ptr)
{
struct dns_query_struct *query = NULL;
int ret = 0;
unsigned int key = 0;
query = malloc(sizeof(*query));
if (query == NULL) {
goto errout;
}
memset(query, 0, sizeof(*query));
INIT_HLIST_NODE(&query->domain_node);
INIT_LIST_HEAD(&query->dns_request_list);
atomic_set(&query->refcnt, 0);
atomic_set(&query->dns_request_sent, 0);
hash_init(query->replied_map);
strncpy(query->domain, domain, DNS_MAX_CNAME_LEN);
query->user_ptr = user_ptr;
query->callback = callback;
query->qtype = qtype;
query->send_tick = 0;
query->sid = atomic_inc_return(&dns_client_sid);
_dns_client_query_get(query);
/* add query to hashtable */
key = hash_string(domain);
key = jhash(&query->sid, sizeof(query->sid), key);
pthread_mutex_lock(&client.domain_map_lock);
list_add_tail(&query->dns_request_list, &client.dns_request_list);
hash_add(client.domain_map, &query->domain_node, key);
pthread_mutex_unlock(&client.domain_map_lock);
/* send query */
ret = _dns_client_send_query(query, domain);
if (ret != 0) {
goto errout_del_list;
}
tlog(TLOG_INFO, "send request %s, id %d\n", domain, query->sid);
return 0;
errout_del_list:
atomic_dec(&query->refcnt);
pthread_mutex_lock(&client.domain_map_lock);
list_del_init(&query->dns_request_list);
hash_del(&query->domain_node);
pthread_mutex_unlock(&client.domain_map_lock);
errout:
if (query) {
tlog(TLOG_ERROR, "release %p", query);
free(query);
}
return -1;
}
static struct addrinfo *_dns_server_getaddr(const char *host, const char *port, 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;
hints.ai_flags = AI_PASSIVE;
if (getaddrinfo(host, port, &hints, &result) != 0) {
tlog(TLOG_ERROR, "get addr info failed. %s\n", strerror(errno));
goto errout;
}
return result;
errout:
if (result) {
freeaddrinfo(result);
}
return NULL;
}
int dns_client_start(void)
{
struct epoll_event event;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN;
event.data.fd = client.udp;
if (epoll_ctl(client.epoll_fd, EPOLL_CTL_ADD, client.udp, &event) != 0) {
tlog(TLOG_ERROR, "epoll ctl failed.");
return -1;
}
return 0;
}
int dns_client_socket(void)
{
int fd = -1;
struct addrinfo *gai = NULL;
/* create udp socket */
gai = _dns_server_getaddr(NULL, "53", SOCK_DGRAM, 0);
if (gai == NULL) {
tlog(TLOG_ERROR, "get address failed.\n");
goto errout;
}
fd = socket(gai->ai_family, gai->ai_socktype, gai->ai_protocol);
if (fd < 0) {
tlog(TLOG_ERROR, "create socket failed.\n");
goto errout;
}
client.udp = fd;
freeaddrinfo(gai);
return fd;
errout:
if (fd > 0) {
close(fd);
}
if (gai) {
freeaddrinfo(gai);
}
return -1;
}
int dns_client_init()
{
pthread_attr_t attr;
int epollfd = -1;
int ret;
int fd = -1;
if (client.epoll_fd > 0) {
return -1;
}
memset(&client, 0, sizeof(client));
pthread_attr_init(&attr);
epollfd = epoll_create1(EPOLL_CLOEXEC);
if (epollfd < 0) {
tlog(TLOG_ERROR, "create epoll failed, %s\n", strerror(errno));
goto errout;
}
fd = dns_client_socket();
if (fd < 0) {
tlog(TLOG_ERROR, "create client socket failed.\n");
goto errout;
}
pthread_mutex_init(&client.server_list_lock, 0);
INIT_LIST_HEAD(&client.dns_server_list);
pthread_mutex_init(&client.domain_map_lock, 0);
hash_init(client.domain_map);
INIT_LIST_HEAD(&client.dns_request_list);
client.epoll_fd = epollfd;
client.run = 1;
client.udp = fd;
/* start work task */
ret = pthread_create(&client.tid, &attr, _dns_client_work, NULL);
if (ret != 0) {
tlog(TLOG_ERROR, "create client work thread failed, %s\n", strerror(errno));
goto errout;
}
if (dns_client_start()) {
tlog(TLOG_ERROR, "start client failed.\n");
goto errout;
}
return 0;
errout:
if (client.tid > 0) {
void *retval = NULL;
client.run = 0;
pthread_join(client.tid, &retval);
}
if (client.udp > 0) {
close(client.udp);
client.udp = -1;
}
if (epollfd) {
close(epollfd);
}
pthread_mutex_destroy(&client.server_list_lock);
pthread_mutex_destroy(&client.domain_map_lock);
return -1;
}
void dns_client_exit()
{
if (client.tid > 0) {
void *ret = NULL;
client.run = 0;
pthread_join(client.tid, &ret);
}
if (client.udp > 0) {
close(client.udp);
}
/* free all resouces */
_dns_client_server_remove_all();
_dns_client_query_remove_all();
pthread_mutex_destroy(&client.server_list_lock);
pthread_mutex_destroy(&client.domain_map_lock);
}

35
src/dns_client.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef _SMART_DNS_CLIENT_H
#define _SMART_DNS_CLIENT_H
#include "dns.h"
typedef enum {
DNS_SERVER_UDP,
DNS_SERVER_TCP,
DNS_SERVER_HTTP,
DNS_SERVER_TYPE_END,
} dns_server_type_t;
typedef enum dns_result_type {
DNS_QUERY_ERR,
DNS_QUERY_RESULT = 1,
DNS_QUERY_END,
} dns_result_type;
int dns_client_init(void);
/* query result notify function */
typedef int (*dns_client_callback)(char *domain, dns_result_type rtype, struct dns_packet *packet, unsigned char *inpacket, int inpacket_len, void *user_ptr);
/* query domain */
int dns_client_query(char *domain, int qtype, dns_client_callback callback, void *user_ptr);
void dns_client_exit(void);
/* add remote dns server */
int dns_add_server(char *server_ip, int port, dns_server_type_t server_type);
/* remove remote dns server */
int dns_remove_server(char *server_ip, int port, dns_server_type_t server_type);
#endif

1073
src/dns_server.c Normal file

File diff suppressed because it is too large Load Diff

12
src/dns_server.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef _SMART_DNS_SERVER_H
#define _SMART_DNS_SERVER_H
int dns_server_init(void);
int dns_server_run(void);
void dns_server_stop(void);
void dns_server_exit(void);
#endif

1076
src/fast_ping.c Normal file

File diff suppressed because it is too large Load Diff

40
src/fast_ping.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef FAST_PING_H
#define FAST_PING_H
#include <sys/time.h>
#include <netdb.h>
#ifdef __cpluscplus
extern "C" {
#endif
typedef enum {
FAST_PING_ICMP = 1,
FAST_PING_ICMP6 = 2,
FAST_PING_TCP,
FAST_PING_UDP
} FAST_PING_TYPE;
typedef enum {
PING_RESULT_RESPONSE = 1,
PING_RESULT_TIMEOUT = 2,
PING_RESULT_END = 3,
} FAST_PING_RESULT;
struct ping_host_struct;
typedef void (*fast_ping_result)(struct ping_host_struct *ping_host, const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, int seqno, struct timeval *tv, void *userptr);
/* start ping */
struct ping_host_struct *fast_ping_start(const char *host, int count, int interval, int timeout, fast_ping_result ping_callback, void *userptr);
/* stop ping */
int fast_ping_stop(struct ping_host_struct *ping_host);
int fast_ping_init(void);
void fast_ping_exit(void);
#ifdef __cpluscplus
}
#endif
#endif // !FAST_PING_H

156
src/include/atomic.h Normal file
View File

@@ -0,0 +1,156 @@
#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_add_and_fetch(&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_sub_and_fetch(&v->counter, 1);
}
/**
* Increment atomic variable
* @param v pointer of type atomic_t
*
* Atomically increments @v by 1.
*/
static inline int atomic_inc_return( atomic_t *v )
{
return __sync_add_and_fetch(&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 int atomic_dec_return( atomic_t *v )
{
return __sync_sub_and_fetch(&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

133
src/include/bitmap.h Normal file
View File

@@ -0,0 +1,133 @@
#ifndef _PERF_BITOPS_H
#define _PERF_BITOPS_H
#include <string.h>
#include <stdlib.h>
#include "bitops.h"
#include "findbit.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 (unsigned long *)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 */

200
src/include/bitops.h Normal file
View File

@@ -0,0 +1,200 @@
#ifndef _GENERIC_BITOPS_H_
#define _GENERIC_BITOPS_H_
#include <unistd.h>
#include <stdint.h>
#include "gcc_builtin.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 ffz(x) __ffs(~(x))
/**
* fls - find last (most-significant) bit set
* @x: the word to search
*
* This is defined the same way as ffs.
* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
*/
/**
* __ffs - find first bit in word.
* @word: The word to search
*
* Undefined if no bit exists, so code should check against 0 first.
*/
static inline unsigned long __ffs(unsigned long word)
{
int num = 0;
#if __BITS_PER_LONG == 64
if ((word & 0xffffffff) == 0) {
num += 32;
word >>= 32;
}
#endif
if ((word & 0xffff) == 0) {
num += 16;
word >>= 16;
}
if ((word & 0xff) == 0) {
num += 8;
word >>= 8;
}
if ((word & 0xf) == 0) {
num += 4;
word >>= 4;
}
if ((word & 0x3) == 0) {
num += 2;
word >>= 2;
}
if ((word & 0x1) == 0)
num += 1;
return num;
}
static inline int fls(int x)
{
return 32 - __builtin_clz(x);
}
/**
* fls64 - find last set bit in a 64-bit word
* @x: the word to search
*
* This is defined in a similar way as the libc and compiler builtin
* ffsll, but returns the position of the most significant set bit.
*
* fls64(value) returns 0 if value is 0 or the position of the last
* set bit if value is nonzero. The last (most significant) bit is
* at position 64.
*/
#if BITS_PER_LONG == 32
static inline int fls64(uint64_t x)
{
uint32_t h = x >> 32;
if (h)
return fls(h) + 32;
return fls(x);
}
#elif BITS_PER_LONG == 64
static inline int fls64(uint64_t x)
{
return 64 - __builtin_clzll(x);
}
#else
#error BITS_PER_LONG not 32 or 64
#endif
/**
* hweightN - returns the hamming weight of a N-bit word
* @x: the word to weigh
*
* The Hamming Weight of a number is the total number of bits set in it.
*/
static inline unsigned int hweight32(unsigned int w)
{
unsigned int res = w - ((w >> 1) & 0x55555555);
res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
res = (res + (res >> 4)) & 0x0F0F0F0F;
res = res + (res >> 8);
return (res + (res >> 16)) & 0x000000FF;
}
static inline unsigned int hweight16(unsigned int w)
{
unsigned int res = w - ((w >> 1) & 0x5555);
res = (res & 0x3333) + ((res >> 2) & 0x3333);
res = (res + (res >> 4)) & 0x0F0F;
return (res + (res >> 8)) & 0x00FF;
}
static inline unsigned int hweight8(unsigned int w)
{
unsigned int res = w - ((w >> 1) & 0x55);
res = (res & 0x33) + ((res >> 2) & 0x33);
return (res + (res >> 4)) & 0x0F;
}
static inline unsigned long hweight64(uint64_t w)
{
#if BITS_PER_LONG == 32
return hweight32((unsigned int)(w >> 32)) + hweight32((unsigned int)w);
#elif BITS_PER_LONG == 64
#ifdef ARCH_HAS_FAST_MULTIPLIER
w -= (w >> 1) & 0x5555555555555555ul;
w = (w & 0x3333333333333333ul) + ((w >> 2) & 0x3333333333333333ul);
w = (w + (w >> 4)) & 0x0f0f0f0f0f0f0f0ful;
return (w * 0x0101010101010101ul) >> 56;
#else
uint64_t res = w - ((w >> 1) & 0x5555555555555555ul);
res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul);
res = (res + (res >> 4)) & 0x0F0F0F0F0F0F0F0Ful;
res = res + (res >> 8);
res = res + (res >> 16);
return (res + (res >> 32)) & 0x00000000000000FFul;
#endif
#endif
}
#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

22
src/include/cache.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef _GENERIC_CACHE_H
#include "list.h"
#include "hashtable.h"
struct cache_node {
struct hlist_node list;
};
struct cache_head;
struct cache_head *cache_new(int hashsize, void (*item_free)(struct cache_head *head, struct cache_node *node));
int cache_add(struct cache_head *head, struct cache_node *node, void *key, int key_len);
struct cache_node *cache_lookup(struct cache_head *head, void *key, int key_len);
int cache_update(struct cache_head *head, void *key, int key_len);
void cache_free(struct cache_head *head);
#endif // !_GENERIC_CACHE_H

77
src/include/findbit.h Normal file
View File

@@ -0,0 +1,77 @@
#ifndef _TOOLS_LINUX_ASM_GENERIC_BITOPS_FIND_H_
#define _TOOLS_LINUX_ASM_GENERIC_BITOPS_FIND_H_
#ifndef find_next_bit
/**
* find_next_bit - find the next set bit in a memory region
* @addr: The address to base the search on
* @offset: The bitnumber to start searching at
* @size: The bitmap size in bits
*
* Returns the bit number for the next set bit
* If no bits are set, returns @size.
*/
extern unsigned long find_next_bit(const unsigned long *addr, unsigned long
size, unsigned long offset);
#endif
#ifndef find_next_and_bit
/**
* find_next_and_bit - find the next set bit in both memory regions
* @addr1: The first address to base the search on
* @addr2: The second address to base the search on
* @offset: The bitnumber to start searching at
* @size: The bitmap size in bits
*
* Returns the bit number for the next set bit
* If no bits are set, returns @size.
*/
extern unsigned long find_next_and_bit(const unsigned long *addr1,
const unsigned long *addr2, unsigned long size,
unsigned long offset);
#endif
#ifndef find_next_zero_bit
/**
* find_next_zero_bit - find the next cleared bit in a memory region
* @addr: The address to base the search on
* @offset: The bitnumber to start searching at
* @size: The bitmap size in bits
*
* Returns the bit number of the next zero bit
* If no bits are zero, returns @size.
*/
unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
unsigned long offset);
#endif
#ifndef find_first_bit
/**
* find_first_bit - find the first set bit in a memory region
* @addr: The address to start the search at
* @size: The maximum number of bits to search
*
* Returns the bit number of the first set bit.
* If no bits are set, returns @size.
*/
extern unsigned long find_first_bit(const unsigned long *addr,
unsigned long size);
#endif /* find_first_bit */
#ifndef find_first_zero_bit
/**
* find_first_zero_bit - find the first cleared bit in a memory region
* @addr: The address to start the search at
* @size: The maximum number of bits to search
*
* Returns the bit number of the first cleared bit.
* If no bits are zero, returns @size.
*/
unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size);
#endif
#endif /*_TOOLS_LINUX_ASM_GENERIC_BITOPS_FIND_H_ */

117
src/include/gcc_builtin.h Normal file
View File

@@ -0,0 +1,117 @@
#ifndef _TOOLS_LINUX_COMPILER_H_
#define _TOOLS_LINUX_COMPILER_H_
#ifndef __compiletime_error
# define __compiletime_error(message)
#endif
/* Optimization barrier */
/* The "volatile" is due to gcc bugs */
#define barrier() __asm__ __volatile__("": : :"memory")
#ifndef __always_inline
# define __always_inline inline __attribute__((always_inline))
#endif
#ifndef noinline
#define noinline
#endif
/* Are two types/vars the same type (ignoring qualifiers)? */
#ifndef __same_type
# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#endif
#ifdef __ANDROID__
/*
* FIXME: Big hammer to get rid of tons of:
* "warning: always_inline function might not be inlinable"
*
* At least on android-ndk-r12/platforms/android-24/arch-arm
*/
#undef __always_inline
#define __always_inline inline
#endif
#define __user
#define __rcu
#define __read_mostly
#ifndef __attribute_const__
# define __attribute_const__
#endif
#ifndef __maybe_unused
# define __maybe_unused __attribute__((unused))
#endif
#ifndef __used
# define __used __attribute__((__unused__))
#endif
#ifndef __packed
# define __packed __attribute__((__packed__))
#endif
#ifndef __force
# define __force
#endif
#ifndef __weak
# define __weak __attribute__((weak))
#endif
#ifndef likely
# define likely(x) __builtin_expect(!!(x), 1)
#endif
#ifndef unlikely
# define unlikely(x) __builtin_expect(!!(x), 0)
#endif
#ifndef __init
# define __init
#endif
#ifndef noinline
# define noinline
#endif
#define uninitialized_var(x) x = *(&(x))
#ifndef __fallthrough
# define __fallthrough
#endif
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#ifndef max
#define max(x, y) ({ \
typeof(x) _max1 = (x); \
typeof(y) _max2 = (y); \
(void) (&_max1 == &_max2); \
_max1 > _max2 ? _max1 : _max2; })
#endif
#ifndef min
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
#endif
#ifndef roundup
#define roundup(x, y) ( \
{ \
const typeof(y) __y = y; \
(((x) + (__y - 1)) / __y) * __y; \
} \
)
#endif
#define __round_mask(x, y) ((__typeof__(x))((y)-1))
#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
#define round_down(x, y) ((x) & ~__round_mask(x, y))
#endif /* _TOOLS_LINUX_COMPILER_H */

213
src/include/hash.h Normal file
View File

@@ -0,0 +1,213 @@
#ifndef _GENERIC_HASH_H
#define _GENERIC_HASH_H
#include "bitmap.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) \
)
#if BITS_PER_LONG == 32
#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_32
#define hash_long(val, bits) hash_32(val, bits)
#elif BITS_PER_LONG == 64
#define hash_long(val, bits) hash_64(val, bits)
#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_64
#else
#error Wordsize not 32 or 64
#endif
/*
* 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 */

150
src/include/hashtable.h Normal file
View File

@@ -0,0 +1,150 @@
/*
* Statically sized hash table implementation
* (C) 2012 Sasha Levin <levinsasha928@gmail.com>
*/
#ifndef _GENERIC_HASHTABLE_H
#define _GENERIC_HASHTABLE_H
#include "list.h"
#include "hash.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
src/include/jhash.h Normal file
View File

@@ -0,0 +1,184 @@
#ifndef _JHASH_H
#define _JHASH_H
#include <stdint.h>
/* 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 = (uint8_t *)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
src/include/list.h Normal file
View File

@@ -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 add 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 *add,
struct list_head *prev,
struct list_head *next)
{
next->prev = add;
add->next = next;
add->prev = prev;
prev->next = add;
}
/**
* list_add - add a add entry
* @add: add entry to be added
* @head: list head to add it after
*
* Insert a add entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *add, struct list_head *head)
{
__list_add(add, head, head->next);
}
/**
* list_add_tail - add a add entry
* @add: add entry to be added
* @head: list head to add it before
*
* Insert a add entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *add, struct list_head *head)
{
__list_add(add, 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 = (struct list_head *)LIST_POISON1;
entry->prev = (struct list_head *)LIST_POISON2;
}
/**
* list_replace - replace old entry by add one
* @old : the element to be replaced
* @add : the add element to insert
*
* If @old was empty, it will be overwritten.
*/
static inline void list_replace(struct list_head *old,
struct list_head *add)
{
add->next = old->next;
add->next->prev = add;
add->prev = old->prev;
add->prev->next = add;
}
static inline void list_replace_init(struct list_head *old,
struct list_head *add)
{
list_replace(old, add);
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 add 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 add 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 add 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 add 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 add 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 = (struct hlist_node *)LIST_POISON1;
n->pprev = (struct hlist_node **)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 *add)
{
add->first = old->first;
if (add->first)
add->first->pprev = &add->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 */

154
src/lib/bitops.c Normal file
View File

@@ -0,0 +1,154 @@
#include "bitmap.h"
#include "bitops.h"
/*
* This is a common helper function for find_next_bit, find_next_zero_bit, and
* find_next_and_bit. The differences are:
* - The "invert" argument, which is XORed with each fetched word before
* searching it for one bits.
* - The optional "addr2", which is anded with "addr1" if present.
*/
static inline unsigned long _find_next_bit(const unsigned long *addr1,
const unsigned long *addr2, unsigned long nbits,
unsigned long start, unsigned long invert)
{
unsigned long tmp;
if (unlikely(start >= nbits))
return nbits;
tmp = addr1[start / BITS_PER_LONG];
if (addr2)
tmp &= addr2[start / BITS_PER_LONG];
tmp ^= invert;
/* Handle 1st word. */
tmp &= BITMAP_FIRST_WORD_MASK(start);
start = round_down(start, BITS_PER_LONG);
while (!tmp) {
start += BITS_PER_LONG;
if (start >= nbits)
return nbits;
tmp = addr1[start / BITS_PER_LONG];
if (addr2)
tmp &= addr2[start / BITS_PER_LONG];
tmp ^= invert;
}
return min(start + __ffs(tmp), nbits);
}
/*
* Find the next set bit in a memory region.
*/
unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
unsigned long offset)
{
return _find_next_bit(addr, NULL, size, offset, 0UL);
}
/*
* Find the first set bit in a memory region.
*/
unsigned long find_first_bit(const unsigned long *addr, unsigned long size)
{
unsigned long idx;
for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
if (addr[idx])
return min(idx * BITS_PER_LONG + __ffs(addr[idx]), size);
}
return size;
}
/*
* Find the first cleared bit in a memory region.
*/
unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size)
{
unsigned long idx;
for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
if (addr[idx] != ~0UL)
return min(idx * BITS_PER_LONG + ffz(addr[idx]), size);
}
return size;
}
unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
unsigned long offset)
{
return _find_next_bit(addr, NULL, size, offset, ~0UL);
}
unsigned long find_next_and_bit(const unsigned long *addr1,
const unsigned long *addr2, unsigned long size,
unsigned long offset)
{
return _find_next_bit(addr1, addr2, size, offset, 0UL);
}
/**
* hweightN - returns the hamming weight of a N-bit word
* @x: the word to weigh
*
* The Hamming Weight of a number is the total number of bits set in it.
*/
unsigned int __sw_hweight32(unsigned int w)
{
#ifdef CONFIG_ARCH_HAS_FAST_MULTIPLIER
w -= (w >> 1) & 0x55555555;
w = (w & 0x33333333) + ((w >> 2) & 0x33333333);
w = (w + (w >> 4)) & 0x0f0f0f0f;
return (w * 0x01010101) >> 24;
#else
unsigned int res = w - ((w >> 1) & 0x55555555);
res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
res = (res + (res >> 4)) & 0x0F0F0F0F;
res = res + (res >> 8);
return (res + (res >> 16)) & 0x000000FF;
#endif
}
unsigned int __sw_hweight16(unsigned int w)
{
unsigned int res = w - ((w >> 1) & 0x5555);
res = (res & 0x3333) + ((res >> 2) & 0x3333);
res = (res + (res >> 4)) & 0x0F0F;
return (res + (res >> 8)) & 0x00FF;
}
unsigned int __sw_hweight8(unsigned int w)
{
unsigned int res = w - ((w >> 1) & 0x55);
res = (res & 0x33) + ((res >> 2) & 0x33);
return (res + (res >> 4)) & 0x0F;
}
unsigned long __sw_hweight64(uint64_t w)
{
#if BITS_PER_LONG == 32
return __sw_hweight32((unsigned int)(w >> 32)) +
__sw_hweight32((unsigned int)w);
#elif BITS_PER_LONG == 64
#ifdef CONFIG_ARCH_HAS_FAST_MULTIPLIER
w -= (w >> 1) & 0x5555555555555555ul;
w = (w & 0x3333333333333333ul) + ((w >> 2) & 0x3333333333333333ul);
w = (w + (w >> 4)) & 0x0f0f0f0f0f0f0f0ful;
return (w * 0x0101010101010101ul) >> 56;
#else
uint64_t res = w - ((w >> 1) & 0x5555555555555555ul);
res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul);
res = (res + (res >> 4)) & 0x0F0F0F0F0F0F0F0Ful;
res = res + (res >> 8);
res = res + (res >> 16);
return (res + (res >> 32)) & 0x00000000000000FFul;
#endif
#endif
}

8
src/lib/cache.c Normal file
View File

@@ -0,0 +1,8 @@
#include "cache.h"
#include <pthread.h>
struct cache_head {
struct hlist_head hash_head;
int hash_size;
pthread_rwlock_t *rwlock;
};

302
src/smartdns.c Normal file
View File

@@ -0,0 +1,302 @@
/*************************************************************************
*
* Copyright (C) 2018 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "atomic.h"
#include "conf.h"
#include "dns_client.h"
#include "dns_server.h"
#include "fast_ping.h"
#include "hashtable.h"
#include "list.h"
#include "tlog.h"
#include "util.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#define RESOLVE_FILE "/etc/resolv.conf"
#define MAX_LINE_LEN 1024
#define MAX_KEY_LEN 64
#define SMARTDNS_CONF_FILE "/etc/smartdns/smartdns.conf"
#define SMARTDNS_LOG_PATH "/var/log"
#define SMARTDNS_LOG_FILE "smartdns.log"
#define SMARTDNS_PID_FILE "/var/run/smartdns.pid"
#define TMP_BUFF_LEN_32 32
void help(void)
{
/* clang-format off */
char *help = ""
"Usage: smartdns [OPTION]...\n"
"Start smartdns server.\n"
" -f run forground.\n"
" -c [conf] config file.\n"
" -h show this help message.\n"
"\n";
/* clang-format on */
printf(help);
}
int smartdns_load_from_resolv(void)
{
FILE *fp = NULL;
char line[MAX_LINE_LEN];
char key[MAX_KEY_LEN];
char value[MAX_LINE_LEN];
char ns_ip[DNS_MAX_IPLEN];
int port = PORT_NOT_DEFINED;
int ret = -1;
int filed_num = 0;
int line_num = 0;
fp = fopen(RESOLVE_FILE, "r");
if (fp == NULL) {
tlog(TLOG_ERROR, "open %s failed, %s", RESOLVE_FILE, strerror(errno));
return -1;
}
while (fgets(line, MAX_LINE_LEN, fp)) {
line_num++;
filed_num = sscanf(line, "%63s %1023[^\r\n]s", key, value);
if (filed_num != 2) {
continue;
}
if (strncmp(key, "nameserver", MAX_KEY_LEN) != 0) {
continue;
}
if (parse_ip(value, ns_ip, &port) != 0) {
continue;
}
if (port == PORT_NOT_DEFINED) {
port = DEFAULT_DNS_PORT;
}
strncpy(dns_conf_servers[dns_conf_server_num].server, ns_ip, DNS_MAX_IPLEN);
dns_conf_servers[dns_conf_server_num].port = port;
dns_conf_servers[dns_conf_server_num].type = DNS_SERVER_UDP;
dns_conf_server_num++;
ret = 0;
}
fclose(fp);
return ret;
}
int smartdns_add_servers(void)
{
int i = 0;
int ret = 0;
for (i = 0; i < dns_conf_server_num; i++) {
ret = dns_add_server(dns_conf_servers[i].server, dns_conf_servers[i].port, dns_conf_servers[i].type);
if (ret != 0) {
tlog(TLOG_ERROR, "add server failed, %s:%d", dns_conf_servers[i].server, dns_conf_servers[i].port);
return -1;
}
}
return 0;
}
int create_pid_file(const char *pid_file)
{
int fd;
int flags;
char buff[TMP_BUFF_LEN_32];
/* create pid file, and lock this file */
fd = open(pid_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
fprintf(stderr, "create pid file failed, %s", strerror(errno));
return -1;
}
flags = fcntl(fd, F_GETFD);
if (flags < 0) {
fprintf(stderr, "Could not get flags for PID file %s", pid_file);
goto errout;
}
flags |= FD_CLOEXEC;
if (fcntl(fd, F_SETFD, flags) == -1) {
fprintf(stderr, "Could not set flags for PID file %s", pid_file);
goto errout;
}
if (lockf(fd, F_TLOCK, 0) < 0) {
fprintf(stderr, "Server is already running.\n");
goto errout;
}
snprintf(buff, TMP_BUFF_LEN_32, "%d\n", getpid());
if (write(fd, buff, strnlen(buff, TMP_BUFF_LEN_32)) < 0) {
fprintf(stderr, "write pid to file failed, %s.\n", strerror(errno));
goto errout;
}
return 0;
errout:
if (fd > 0) {
close(fd);
}
return -1;
}
int smartdns_init(void)
{
int ret;
ret = tlog_init(SMARTDNS_LOG_PATH, SMARTDNS_LOG_FILE, 1024 * 1024, 8, 1, 0, 0);
if (ret != 0) {
tlog(TLOG_ERROR, "start tlog failed.\n");
goto errout;
}
tlog_setlogscreen(1);
tlog_setlevel(dns_conf_loglevel);
if (dns_conf_server_num <= 0) {
if (smartdns_load_from_resolv() != 0) {
tlog(TLOG_ERROR, "load dns from resolv failed.");
goto errout;
}
}
ret = fast_ping_init();
if (ret != 0) {
tlog(TLOG_ERROR, "start ping failed.\n");
goto errout;
}
ret = dns_server_init();
if (ret != 0) {
tlog(TLOG_ERROR, "start dns server failed.\n");
goto errout;
}
ret = dns_client_init();
if (ret != 0) {
tlog(TLOG_ERROR, "start dns client failed.\n");
goto errout;
}
ret = smartdns_add_servers();
if (ret != 0) {
tlog(TLOG_ERROR, "add servers failed.");
goto errout;
}
return 0;
errout:
return -1;
}
int smartdns_run(void)
{
return dns_server_run();
}
void smartdns_exit(void)
{
dns_server_exit();
dns_client_exit();
fast_ping_exit();
tlog_exit();
}
void sig_handle(int sig)
{
switch (sig) {
case SIGINT:
dns_server_stop();
return;
break;
default:
break;
}
tlog(TLOG_ERROR, "process exit.\n");
_exit(0);
}
int main(int argc, char *argv[])
{
int ret;
int is_forground = 0;
int opt;
char config_file[MAX_LINE_LEN];
strncpy(config_file, SMARTDNS_CONF_FILE, MAX_LINE_LEN);
while ((opt = getopt(argc, argv, "fhc:")) != -1) {
switch (opt) {
case 'f':
is_forground = 1;
break;
case 'c':
snprintf(config_file, sizeof(config_file), optarg);
break;
case 'h':
help();
return 1;
}
}
if (is_forground == 0) {
if (daemon(0, 0) < 0) {
fprintf(stderr, "run daemon process failed, %s\n", strerror(errno));
return 1;
}
}
signal(SIGABRT, sig_handle);
if (load_conf(config_file) != 0) {
}
if (create_pid_file(SMARTDNS_PID_FILE) != 0) {
fprintf(stderr, "create pid file failed, %s\n", strerror(errno));
goto errout;
}
ret = smartdns_init();
if (ret != 0) {
usleep(100000);
goto errout;
}
signal(SIGINT, sig_handle);
atexit(smartdns_exit);
return smartdns_run();
errout:
return 1;
}

908
src/tlog.c Normal file
View File

@@ -0,0 +1,908 @@
/*
* Copyright (C) 2018 Ruilin Peng (Nick) <pymumu@gmail.com>
*/
#include "tlog.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define TLOG_BUFF_SIZE (1024 * 128)
#define TLOG_MAX_LINE_LEN (1024)
#define TLOG_TMP_LEN 128
#define TLOG_LOG_SIZE (1024 * 1024 * 50)
#define TLOG_LOG_COUNT 32
struct oldest_log {
char name[TLOG_TMP_LEN];
time_t mtime;
};
struct tlog {
char *buff;
int buffsize;
int start;
int end;
int ext_end;
int run;
pthread_t tid;
pthread_mutex_t lock;
pthread_cond_t cond;
pthread_cond_t client_cond;
int waiters;
int is_wait;
int fd;
int fd_lock;
off_t filesize;
char logdir[PATH_MAX];
char logname[PATH_MAX];
int logsize;
int logcount;
int block;
int dropped;
int zip_pid;
int multi_log;
int logscreen;
};
typedef int (*list_callback)(const char *name, struct dirent *entry, void *user);
struct tlog tlog;
static tlog_level tlog_set_level = TLOG_INFO;
tlog_format_func tlog_format;
static unsigned int tlog_localtime_lock = 0;
static const char *tlog_level_str[] = {
"DEBUG",
"INFO",
"NOTICE",
"WARN",
"ERROR",
"FATAL",
};
static inline void _tlog_spin_lock(unsigned int *lock)
{
while (1) {
int i;
for (i = 0; i < 10000; i++) {
if (__sync_bool_compare_and_swap(lock, 0, 1)) {
return;
}
}
sched_yield();
}
}
static inline void _tlog_spin_unlock(unsigned int *lock)
{
__sync_bool_compare_and_swap(lock, 1, 0);
}
static int _tlog_mkdir(const char *path)
{
char path_c[PATH_MAX];
char *path_end;
char str;
int len;
if (access(path, F_OK) == 0) {
return 0;
}
strncpy(path_c, path, sizeof(path_c) - 1);
len = strnlen(path_c, sizeof(path_c) - 1);
path_c[len] = '/';
path_c[len + 1] = '\0';
path_end = path_c;
/* create directory recursively */
while (*path_end != 0) {
if (*path_end != '/') {
path_end++;
continue;
}
str = *path_end;
*path_end = '\0';
if (access(path_c, F_OK) == 0) {
*path_end = str;
path_end++;
continue;
}
if (mkdir(path_c, 0750) != 0) {
fprintf(stderr, "create directory %s failed, %s\n", path_c, strerror(errno));
return -1;
}
*path_end = str;
path_end++;
}
return 0;
}
static struct tm *_tlog_localtime(time_t *timep, struct tm *tm)
{
static time_t last_time = {0};
static struct tm last_tm = {0};
/* localtime_r has a global timezone lock, it's about 8 times slower than gmtime
* this code is used to speed up localtime_r call.
*/
_tlog_spin_lock(&tlog_localtime_lock);
if (*timep == last_time) {
*tm = last_tm;
} else {
_tlog_spin_unlock(&tlog_localtime_lock);
tm = localtime_r(timep, tm);
_tlog_spin_lock(&tlog_localtime_lock);
if (tm) {
last_time = *timep;
last_tm = *tm;
}
}
_tlog_spin_unlock(&tlog_localtime_lock);
return tm;
}
static int _tlog_getmtime(struct tlog_time *log_mtime, const char *file)
{
struct tm tm;
struct stat sb;
if (stat(file, &sb) != 0) {
return -1;
}
if (_tlog_localtime(&sb.st_mtime, &tm) == NULL) {
return -1;
}
log_mtime->year = tm.tm_year + 1900;
log_mtime->mon = tm.tm_mon + 1;
log_mtime->mday = tm.tm_mday;
log_mtime->hour = tm.tm_hour;
log_mtime->min = tm.tm_min;
log_mtime->sec = tm.tm_sec;
log_mtime->usec = 0;
return 0;
}
static int _tlog_gettime(struct tlog_time *cur_time)
{
struct tm tm;
struct timeval tmval;
if (gettimeofday(&tmval, NULL) != 0) {
return -1;
}
if (_tlog_localtime(&tmval.tv_sec, &tm) == NULL) {
return -1;
}
cur_time->year = tm.tm_year + 1900;
cur_time->mon = tm.tm_mon + 1;
cur_time->mday = tm.tm_mday;
cur_time->hour = tm.tm_hour;
cur_time->min = tm.tm_min;
cur_time->sec = tm.tm_sec;
cur_time->usec = tmval.tv_usec;
return 0;
}
static int _tlog_format(char *buff, int maxlen, struct tlog_info *info, void *userptr, const char *format, va_list ap)
{
int len = 0;
int total_len = 0;
struct tlog_time *tm = &info->time;
if (tlog.multi_log) {
/* format prefix */
len = snprintf(buff, maxlen, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d][%5d][%4s][%17s:%-4d] ",
tm->year, tm->mon, tm->mday, tm->hour, tm->min, tm->sec, tm->usec / 1000, getpid(),
info->level, info->file, info->line);
} else {
/* format prefix */
len = snprintf(buff, maxlen, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d][%5s][%17s:%-4d] ",
tm->year, tm->mon, tm->mday, tm->hour, tm->min, tm->sec, tm->usec / 1000,
info->level, info->file, info->line);
}
if (len < 0 || len == maxlen) {
return -1;
}
buff += len;
total_len += len;
maxlen -= len;
/* format log message */
len = vsnprintf(buff, maxlen, format, ap);
if (len < 0 || len == maxlen) {
return -1;
}
buff += len;
total_len += len;
/* return total length */
return total_len;
}
static int _tlog_log_buffer(char *buff, int maxlen, tlog_level level, const char *file, int line, const char *func, void *userptr, const char *format, va_list ap)
{
int len;
struct tlog_info info;
if (tlog_format == NULL) {
return -1;
}
if (level >= TLOG_END) {
return -1;
}
info.file = file;
info.line = line;
info.func = func;
info.level = tlog_level_str[level];
if (_tlog_gettime(&info.time) != 0) {
return -1;
}
len = tlog_format(buff, maxlen, &info, userptr, format, ap);
if (len < 0) {
return -1;
}
/* add new line character*/
if (*(buff + len - 1) != '\n' && len + 1 < maxlen - len) {
*(buff + len) = '\n';
len++;
}
return len;
}
int tlog_vext(tlog_level level, const char *file, int line, const char *func, void *userptr, const char *format, va_list ap)
{
int len;
int maxlen = 0;
if (tlog.buff == NULL) {
return -1;
}
if (level < tlog_set_level) {
return 0;
}
pthread_mutex_lock(&tlog.lock);
do {
if (tlog.end == tlog.start) {
if (tlog.ext_end == 0) {
/* if buffer is empty */
maxlen = tlog.buffsize - tlog.end;
}
} else if (tlog.end > tlog.start) {
maxlen = tlog.buffsize - tlog.end;
} else {
/* if reverse */
maxlen = tlog.start - tlog.end;
}
/* if free buffer length is less than min line length */
if (maxlen < TLOG_MAX_LINE_LEN) {
if (tlog.end != tlog.start) {
pthread_cond_signal(&tlog.cond);
}
/* if drop message, increase statistics and return */
if (tlog.block == 0) {
tlog.dropped++;
pthread_mutex_unlock(&tlog.lock);
return -1;
}
tlog.waiters++;
/* block wait for free buffer */
int ret = pthread_cond_wait(&tlog.client_cond, &tlog.lock);
tlog.waiters--;
if (ret < 0) {
pthread_mutex_unlock(&tlog.lock);
return -1;
}
}
} while (maxlen < TLOG_MAX_LINE_LEN);
/* write log to buffer */
len = _tlog_log_buffer(tlog.buff + tlog.end, maxlen, level, file, line, func, userptr, format, ap);
if (len <= 0) {
pthread_mutex_unlock(&tlog.lock);
return -1;
}
tlog.end += len;
/* if remain buffer is not enough for a line, move end to start of buffer. */
if (tlog.end > tlog.buffsize - TLOG_MAX_LINE_LEN) {
tlog.ext_end = tlog.end;
tlog.end = 0;
}
if (tlog.is_wait) {
pthread_cond_signal(&tlog.cond);
}
pthread_mutex_unlock(&tlog.lock);
return len;
}
int tlog_ext(tlog_level level, const char *file, int line, const char *func, void *userptr,
const char *format, ...)
{
int len;
va_list ap;
va_start(ap, format);
len = tlog_vext(level, file, line, func, userptr, format, ap);
va_end(ap);
return len;
}
static int _tlog_rename_logfile(const char *gzip_file)
{
char archive_file[PATH_MAX];
struct tlog_time logtime;
int i = 0;
if (_tlog_getmtime(&logtime, gzip_file) != 0) {
return -1;
}
snprintf(archive_file, sizeof(archive_file), "%s/%s-%.4d%.2d%.2d-%.2d%.2d%.2d.gz",
tlog.logdir, tlog.logname, logtime.year, logtime.mon, logtime.mday,
logtime.hour, logtime.min, logtime.sec);
while (access(archive_file, F_OK) == 0) {
i++;
snprintf(archive_file, sizeof(archive_file), "%s/%s-%.4d%.2d%.2d-%.2d%.2d%.2d-%d.gz",
tlog.logdir, tlog.logname, logtime.year, logtime.mon, logtime.mday,
logtime.hour, logtime.min, logtime.sec, i);
}
if (rename(gzip_file, archive_file) != 0) {
return -1;
}
return 0;
}
static int _tlog_list_dir(const char *path, list_callback callback, void *userptr)
{
DIR *dir = NULL;
struct dirent *ent;
int ret = 0;
dir = opendir(path);
if (dir == NULL) {
fprintf(stderr, "open directory failed, %s\n", strerror(errno));
goto errout;
}
while ((ent = readdir(dir)) != NULL) {
if (strncmp(".", ent->d_name, 2) == 0 || strncmp("..", ent->d_name, 3) == 0) {
continue;
}
ret = callback(path, ent, userptr);
if (ret != 0) {
goto errout;
}
}
closedir(dir);
return 0;
errout:
if (dir) {
closedir(dir);
dir = NULL;
}
return -1;
}
static int _tlog_count_log_callback(const char *path, struct dirent *entry, void *userptr)
{
int *lognum = (int *)userptr;
if (strstr(entry->d_name, ".gz") == NULL) {
return 0;
}
int len = strnlen(tlog.logname, sizeof(tlog.logname));
if (strncmp(tlog.logname, entry->d_name, len) != 0) {
return 0;
}
(*lognum)++;
return 0;
}
static int _tlog_get_oldest_callback(const char *path, struct dirent *entry, void *userptr)
{
struct stat sb;
char filename[PATH_MAX];
struct oldest_log *oldestlog = userptr;
/* if not a gz file, skip */
if (strstr(entry->d_name, ".gz") == NULL) {
return 0;
}
/* if not tlog gz file, skip */
int len = strnlen(tlog.logname, sizeof(tlog.logname));
if (strncmp(tlog.logname, entry->d_name, len) != 0) {
return 0;
}
/* get log file mtime */
snprintf(filename, sizeof(filename), "%s/%s", path, entry->d_name);
if (stat(filename, &sb) != 0) {
return -1;
}
if (oldestlog->mtime == 0 || oldestlog->mtime > sb.st_mtime) {
oldestlog->mtime = sb.st_mtime;
strncpy(oldestlog->name, entry->d_name, sizeof(oldestlog->name));
return 0;
}
return 0;
}
static int _tlog_remove_oldestlog(void)
{
struct oldest_log oldestlog;
oldestlog.name[0] = 0;
oldestlog.mtime = 0;
/* get oldest log file name */
if (_tlog_list_dir(tlog.logdir, _tlog_get_oldest_callback, &oldestlog) != 0) {
return -1;
}
char filename[PATH_MAX];
snprintf(filename, sizeof(filename), "%s/%s", tlog.logdir, oldestlog.name);
/* delete */
unlink(filename);
return 0;
}
static int _tlog_remove_oldlog(void)
{
int lognum = 0;
int i = 0;
/* get total log file number */
if (_tlog_list_dir(tlog.logdir, _tlog_count_log_callback, &lognum) != 0) {
fprintf(stderr, "get log file count failed.\n");
return -1;
}
/* remove last N log files */
for (i = 0; i < lognum - tlog.logcount; i++) {
_tlog_remove_oldestlog();
}
return 0;
}
static void _tlog_log_unlock(void)
{
char lock_file[PATH_MAX];
if (tlog.fd_lock <= 0) {
return;
}
snprintf(lock_file, sizeof(lock_file), "%s/%s.lock", tlog.logdir, tlog.logname);
unlink(lock_file);
close(tlog.fd_lock);
tlog.fd_lock = -1;
}
static int _tlog_log_lock(void)
{
char lock_file[PATH_MAX];
int fd;
if (tlog.multi_log == 0) {
return 0;
}
snprintf(lock_file, sizeof(lock_file), "%s/%s.lock", tlog.logdir, tlog.logname);
fd = open(lock_file, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
if (fd == -1) {
fprintf(stderr, "create pid file failed, %s", strerror(errno));
return -1;
}
if (lockf(fd, F_TLOCK, 0) < 0) {
goto errout;
}
tlog.fd_lock = fd;
return 0;
errout:
if (fd > 0) {
close(fd);
}
return -1;
}
static void _tlog_wait_pid(int wait_hang)
{
int status;
if (tlog.zip_pid <= 0) {
return;
}
int option = (wait_hang == 0) ? WNOHANG : 0;
/* check and obtain gzip process status*/
if (waitpid(tlog.zip_pid, &status, option) <= 0) {
return;
}
/* gzip process exited */
tlog.zip_pid = -1;
char gzip_file[PATH_MAX];
/* rename ziped file */
snprintf(gzip_file, sizeof(gzip_file), "%s/%s.pending.gz", tlog.logdir, tlog.logname);
if (_tlog_rename_logfile(gzip_file) != 0) {
_tlog_log_unlock();
return;
}
/* remove oldes file */
_tlog_remove_oldlog();
_tlog_log_unlock();
}
static int _tlog_archive_log(void)
{
char gzip_file[PATH_MAX];
char gzip_cmd[PATH_MAX];
char log_file[PATH_MAX];
char pending_file[PATH_MAX];
snprintf(gzip_file, sizeof(gzip_file), "%s/%s.pending.gz", tlog.logdir, tlog.logname);
snprintf(pending_file, sizeof(pending_file), "%s/%s.pending", tlog.logdir, tlog.logname);
if (_tlog_log_lock() != 0) {
return -1;
}
/* if pending.zip exists */
if (access(gzip_file, F_OK) == 0) {
/* rename it to standard name */
if (_tlog_rename_logfile(gzip_file) != 0) {
goto errout;
}
}
if (access(pending_file, F_OK) != 0) {
/* rename current log file to pending */
snprintf(log_file, sizeof(log_file), "%s/%s", tlog.logdir, tlog.logname);
if (rename(log_file, pending_file) != 0) {
goto errout;
}
}
/* start gzip process to compress log file */
snprintf(gzip_cmd, sizeof(gzip_cmd), "gzip -1 %s", pending_file);
if (tlog.zip_pid <= 0) {
int pid = vfork();
if (pid == 0) {
execl("/bin/sh", "sh", "-c", gzip_cmd, NULL);
_exit(1);
} else if (pid < 0) {
goto errout;
}
tlog.zip_pid = pid;
}
return 0;
errout:
_tlog_log_unlock();
return -1;
}
static int _tlog_write_log(char *buff, int bufflen)
{
int len;
/* if log file size exceeds threshold, start to compress */
if (tlog.multi_log) {
tlog.filesize = lseek(tlog.fd, 0, SEEK_END);
}
if (tlog.filesize > tlog.logsize && tlog.zip_pid <= 0) {
if (tlog.filesize < lseek(tlog.fd, 0, SEEK_END) && tlog.multi_log == 0) {
const char *msg = "[Auto enable multi-process write mode, log may be lost, please enable multi-process write mode manually]\n";
tlog.multi_log = 1;
write(tlog.fd, msg, strlen(msg));
}
close(tlog.fd);
tlog.fd = -1;
tlog.filesize = 0;
_tlog_archive_log();
}
if (tlog.fd <= 0) {
/* open a new log file to write */
char logfile[PATH_MAX];
if (_tlog_mkdir(tlog.logdir) != 0) {
fprintf(stderr, "create log dir %s failed.\n", tlog.logdir);
return -1;
}
snprintf(logfile, sizeof(logfile), "%s/%s", tlog.logdir, tlog.logname);
tlog.filesize = 0;
tlog.fd = open(logfile, O_APPEND | O_CREAT | O_WRONLY | O_CLOEXEC, 0640);
if (tlog.fd < 0) {
fprintf(stderr, "open log file %s failed, %s\n", logfile, strerror(errno));
return -1;
}
/* get log file size */
tlog.filesize = lseek(tlog.fd, 0, SEEK_END);
}
/* output log to screen */
if (tlog.logscreen) {
write(STDOUT_FILENO, buff, bufflen);
}
/* write log to file */
len = write(tlog.fd, buff, bufflen);
if (len > 0) {
tlog.filesize += len;
}
return len;
}
static void *_tlog_work(void *arg)
{
int ret = 0;
int log_len;
int log_extlen;
int log_end;
int log_extend;
int log_dropped;
struct timespec tm;
time_t now = time(0);
time_t last = now;
while (tlog.run || tlog.end != tlog.start || tlog.ext_end > 0) {
log_len = 0;
log_end = 0;
log_extlen = 0;
log_extend = 0;
/* if compressing */
if (tlog.zip_pid > 0) {
now = time(0);
if (now != last) {
/* try to archive compressed file */
_tlog_wait_pid(0);
last = now;
}
}
pthread_mutex_lock(&tlog.lock);
if (tlog.end == tlog.start && tlog.ext_end == 0) {
/* if buffer is empty, wait */
clock_gettime(CLOCK_REALTIME, &tm);
tm.tv_sec += 5;
tlog.is_wait = 1;
ret = pthread_cond_timedwait(&tlog.cond, &tlog.lock, &tm);
tlog.is_wait = 0;
if (ret < 0 || ret == ETIMEDOUT) {
pthread_mutex_unlock(&tlog.lock);
if (ret == ETIMEDOUT) {
continue;
}
sleep(1);
continue;
}
}
if (tlog.ext_end > 0) {
log_len = tlog.ext_end - tlog.start;
log_extend = tlog.ext_end;
}
if (tlog.end < tlog.start) {
log_extlen = tlog.end;
} else if (tlog.end > tlog.start) {
log_len = tlog.end - tlog.start;
}
log_end = tlog.end;
log_dropped = tlog.dropped;
tlog.dropped = 0;
pthread_mutex_unlock(&tlog.lock);
/* write log */
_tlog_write_log(tlog.buff + tlog.start, log_len);
if (log_extlen > 0) {
/* write extend buffer log */
_tlog_write_log(tlog.buff, log_extlen);
}
if (log_dropped > 0) {
/* if there is dropped log, record dropped log number */
char dropmsg[TLOG_TMP_LEN];
snprintf(dropmsg, sizeof(dropmsg), "[Totoal Dropped %d Messages]\n", log_dropped);
_tlog_write_log(dropmsg, strnlen(dropmsg, sizeof(dropmsg)));
}
pthread_mutex_lock(&tlog.lock);
/* release finished buffer */
tlog.start = log_end;
if (log_extend > 0) {
tlog.ext_end = 0;
}
if (tlog.waiters > 0) {
/* if there are waiters, wakeup */
pthread_cond_broadcast(&tlog.client_cond);
}
pthread_mutex_unlock(&tlog.lock);
/* sleep for a while to reduce cpu usage */
usleep(20 * 1000);
}
return NULL;
}
void tlog_setlogscreen(int enable)
{
tlog.logscreen = (enable != 0) ? 1 : 0;
}
int tlog_reg_format_func(tlog_format_func callback)
{
tlog_format = callback;
return 0;
}
int tlog_setlevel(tlog_level level)
{
if (level >= TLOG_END) {
return -1;
}
tlog_set_level = level;
return 0;
}
int tlog_init(const char *logdir, const char *logname, int maxlogsize, int maxlogcount, int block, int buffsize, int multiwrite)
{
pthread_attr_t attr;
int ret;
if (tlog_format != NULL) {
fprintf(stderr, "tlog already initilized.\n");
return -1;
}
if (buffsize > 0 && buffsize < TLOG_MAX_LINE_LEN * 2) {
fprintf(stderr, "buffer size is invalid.\n");
return -1;
}
tlog_format = _tlog_format;
tlog.logscreen = 0;
tlog.buffsize = (buffsize > 0) ? buffsize : TLOG_BUFF_SIZE;
tlog.start = 0;
tlog.end = 0;
tlog.ext_end = 0;
tlog.block = (block != 0) ? 1 : 0;
tlog.waiters = 0;
tlog.dropped = 0;
tlog.logsize = (maxlogsize > 0) ? maxlogsize : TLOG_LOG_SIZE;
tlog.logcount = (maxlogcount > 0) ? maxlogcount : TLOG_LOG_COUNT;
tlog.fd = -1;
tlog.filesize = 0;
tlog.zip_pid = -1;
tlog.logscreen = 0;
tlog.multi_log = (multiwrite != 0) ? 1 : 0;
tlog.is_wait = 0;
pthread_attr_init(&attr);
pthread_mutex_init(&tlog.lock, 0);
pthread_cond_init(&tlog.cond, 0);
pthread_cond_init(&tlog.client_cond, 0);
tlog.buff = malloc(tlog.buffsize);
if (tlog.buff == NULL) {
fprintf(stderr, "malloc tlog buffer failed, %s\n", strerror(errno));
goto errout;
}
strncpy(tlog.logdir, logdir, sizeof(tlog.logdir));
strncpy(tlog.logname, logname, sizeof(tlog.logname));
tlog.run = 1;
ret = pthread_create(&tlog.tid, &attr, _tlog_work, NULL);
if (ret != 0) {
fprintf(stderr, "create tlog work thread failed, %s\n", strerror(errno));
goto errout;
}
return 0;
errout:
if (tlog.buff) {
free(tlog.buff);
tlog.buff = NULL;
}
if (tlog.tid > 0) {
void *retval = NULL;
tlog.run = 0;
pthread_join(tlog.tid, &retval);
}
pthread_cond_destroy(&tlog.client_cond);
pthread_mutex_destroy(&tlog.lock);
pthread_cond_destroy(&tlog.cond);
tlog.run = 0;
return -1;
}
void tlog_exit(void)
{
if (tlog.tid > 0) {
void *ret = NULL;
tlog.run = 0;
pthread_mutex_lock(&tlog.lock);
pthread_cond_signal(&tlog.cond);
pthread_mutex_unlock(&tlog.lock);
pthread_join(tlog.tid, &ret);
}
if (tlog.zip_pid > 0) {
_tlog_wait_pid(1);
}
if (tlog.buff) {
free(tlog.buff);
tlog.buff = NULL;
}
if (tlog.fd > 0) {
close(tlog.fd);
tlog.fd = -1;
}
_tlog_log_unlock();
pthread_cond_destroy(&tlog.client_cond);
pthread_mutex_destroy(&tlog.lock);
pthread_cond_destroy(&tlog.cond);
}

89
src/tlog.h Normal file
View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2018 Ruilin Peng (Nick) <pymumu@gmail.com>
*/
#ifndef TLOG_H
#define TLOG_H
#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
#endif /*__cplusplus */
typedef enum {
TLOG_DEBUG = 0,
TLOG_INFO = 1,
TLOG_NOTICE = 2,
TLOG_WARN = 3,
TLOG_ERROR = 4,
TLOG_FATAL = 5,
TLOG_END = 6
} tlog_level;
struct tlog_time {
int year;
int mon;
int mday;
int hour;
int min;
int sec;
int usec;
};
struct tlog_info {
const char *level;
const char *file;
const char *func;
int line;
struct tlog_time time;
};
/*
FunctionPrint log
level: Current log Levels
format: Log formats
*/
#ifndef BASE_FILE_NAME
#define BASE_FILE_NAME __FILE__
#endif
#define tlog(level, format, ...) tlog_ext(level, BASE_FILE_NAME, __LINE__, __func__, 0, format, ##__VA_ARGS__)
extern int tlog_ext(tlog_level level, const char *file, int line, const char *func, void *userptr, const char *format, ...) __attribute__((format(printf, 6, 7)));
extern int tlog_vext(tlog_level level, const char *file, int line, const char *func, void *userptr, const char *format, va_list ap);
/* set log level */
extern int tlog_setlevel(tlog_level level);
/* enalbe log to screen */
extern void tlog_setlogscreen(int enable);
/*
FunctionInitialize log module
logdir: Log Output path.
logname: Log name.
maxlogsize: The maximum size of a single log file.
maxlogcount: Number of archived logs.
block: Blocked if buffer is not sufficient.
buffsize: Buffer size, zero for default (128K)
multiwrite: enable multi process write mode.
NOTICE: maxlogsize in all prcesses must be same when enable this mode.
*/
extern int tlog_init(const char *logdir, const char *logname, int maxlogsize, int maxlogcount, int block, int buffsize, int multiwrite);
extern void tlog_exit(void);
/*
customize log output format
steps:
1. define format function, function must be defined as tlog_format_func, use snprintf or vsnprintf format log to buffer
2. call tlog_reg_format_func to register format function.
read _tlog_format for example.
*/
typedef int (*tlog_format_func)(char *buff, int maxlen, struct tlog_info *info, void *userptr, const char *format, va_list ap);
extern int tlog_reg_format_func(tlog_format_func func);
#ifdef __cplusplus
}
#endif /*__cplusplus */
#endif // !TLOG_H

101
src/util.c Normal file
View File

@@ -0,0 +1,101 @@
#include "util.h"
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
unsigned long get_tick_count(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
}
char *gethost_by_addr(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));
inet_ntop(AF_INET, &addr_in4.sin_addr, host, addr_len);
} else {
inet_ntop(AF_INET6, &addr_in6->sin6_addr, host, addr_len);
}
} break;
default:
goto errout;
break;
}
return host;
errout:
return NULL;
}
int parse_ip(const char *value, char *ip, int *port)
{
int offset = 0;
char *colon = NULL;
colon = strstr(value, ":");
if (strstr(value, "[")) {
/* ipv6 with port */
char *bracket_end = strstr(value, "]");
if (bracket_end == NULL) {
return -1;
}
offset = bracket_end - value - 1;
memcpy(ip, value + 1, offset);
ip[offset] = 0;
colon = strstr(bracket_end, ":");
if (colon) {
colon++;
}
} else if (colon && strstr(colon + 1, ":")) {
/* ipv6 without port */
strncpy(ip, value, MAX_IP_LEN);
colon = NULL;
} else {
/* ipv4 */
colon = strstr(value, ":");
if (colon == NULL) {
/* without port */
strncpy(ip, value, MAX_IP_LEN);
} else {
/* with port */
offset = colon - value;
colon++;
memcpy(ip, value, offset);
ip[offset] = 0;
}
}
if (colon) {
/* get port num */
*port = atoi(colon);
} else {
*port = PORT_NOT_DEFINED;
}
if (ip[0] == 0) {
return -1;
}
return 0;
}

17
src/util.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef SMART_DNS_UTIL_H
#define SMART_DNS_UTIL_H
#include <netdb.h>
#define PORT_NOT_DEFINED -1
#define MAX_IP_LEN 64
unsigned long get_tick_count(void);
char *gethost_by_addr(char *host, struct sockaddr *addr, socklen_t addr_len);
int parse_ip(const char *value, char *ip, int *port);
#endif