Codesytle: replace CRLF with LF
This commit is contained in:
98
src/Makefile
98
src/Makefile
@@ -1,49 +1,49 @@
|
||||
|
||||
# Copyright (C) 2018-2020 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/>.
|
||||
|
||||
BIN=smartdns
|
||||
OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o
|
||||
OBJS=smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o $(OBJS_LIB)
|
||||
|
||||
# cflags
|
||||
ifndef CFLAGS
|
||||
CFLAGS =-O2 -g -Wall -Wstrict-prototypes -fno-omit-frame-pointer -Wstrict-aliasing
|
||||
endif
|
||||
override CFLAGS +=-Iinclude
|
||||
override CFLAGS += -DBASE_FILE_NAME=\"$(notdir $<)\"
|
||||
ifdef VER
|
||||
override CFLAGS += -DSMARTDNS_VERION=\"$(VER)\"
|
||||
endif
|
||||
|
||||
CXXFLAGS=-O2 -g -Wall -std=c++11
|
||||
override CXXFLAGS +=-Iinclude
|
||||
|
||||
# ldflags
|
||||
ifeq ($(STATIC), yes)
|
||||
override LDFLAGS += -lssl -lcrypto -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -ldl -static
|
||||
else
|
||||
override LDFLAGS += -lssl -lcrypto -lpthread
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
|
||||
all: $(BIN)
|
||||
|
||||
$(BIN) : $(OBJS)
|
||||
$(CC) $(OBJS) -o $@ $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJS) $(BIN)
|
||||
|
||||
# Copyright (C) 2018-2020 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/>.
|
||||
|
||||
BIN=smartdns
|
||||
OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o
|
||||
OBJS=smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o $(OBJS_LIB)
|
||||
|
||||
# cflags
|
||||
ifndef CFLAGS
|
||||
CFLAGS =-O2 -g -Wall -Wstrict-prototypes -fno-omit-frame-pointer -Wstrict-aliasing
|
||||
endif
|
||||
override CFLAGS +=-Iinclude
|
||||
override CFLAGS += -DBASE_FILE_NAME=\"$(notdir $<)\"
|
||||
ifdef VER
|
||||
override CFLAGS += -DSMARTDNS_VERION=\"$(VER)\"
|
||||
endif
|
||||
|
||||
CXXFLAGS=-O2 -g -Wall -std=c++11
|
||||
override CXXFLAGS +=-Iinclude
|
||||
|
||||
# ldflags
|
||||
ifeq ($(STATIC), yes)
|
||||
override LDFLAGS += -lssl -lcrypto -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -ldl -static
|
||||
else
|
||||
override LDFLAGS += -lssl -lcrypto -lpthread
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
|
||||
all: $(BIN)
|
||||
|
||||
$(BIN) : $(OBJS)
|
||||
$(CC) $(OBJS) -o $@ $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJS) $(BIN)
|
||||
|
||||
772
src/dns_cache.c
772
src/dns_cache.c
@@ -1,386 +1,386 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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_cache.h"
|
||||
#include "stringutil.h"
|
||||
#include "tlog.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#define DNS_CACHE_MAX_HITNUM 5000
|
||||
#define DNS_CACHE_HITNUM_STEP 2
|
||||
#define DNS_CACHE_HITNUM_STEP_MAX 6
|
||||
|
||||
struct dns_cache_head {
|
||||
DECLARE_HASHTABLE(cache_hash, 10);
|
||||
struct list_head cache_list;
|
||||
atomic_t num;
|
||||
int size;
|
||||
pthread_mutex_t lock;
|
||||
};
|
||||
|
||||
static struct dns_cache_head dns_cache_head;
|
||||
|
||||
int dns_cache_init(int size)
|
||||
{
|
||||
INIT_LIST_HEAD(&dns_cache_head.cache_list);
|
||||
hash_init(dns_cache_head.cache_hash);
|
||||
atomic_set(&dns_cache_head.num, 0);
|
||||
dns_cache_head.size = size;
|
||||
|
||||
pthread_mutex_init(&dns_cache_head.lock, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) struct dns_cache *_dns_cache_last(void)
|
||||
{
|
||||
return list_last_entry(&dns_cache_head.cache_list, struct dns_cache, list);
|
||||
}
|
||||
|
||||
static struct dns_cache *_dns_cache_first(void)
|
||||
{
|
||||
return list_first_entry_or_null(&dns_cache_head.cache_list, struct dns_cache, list);
|
||||
}
|
||||
|
||||
static void _dns_cache_delete(struct dns_cache *dns_cache)
|
||||
{
|
||||
hash_del(&dns_cache->node);
|
||||
list_del_init(&dns_cache->list);
|
||||
atomic_dec(&dns_cache_head.num);
|
||||
free(dns_cache);
|
||||
}
|
||||
|
||||
void dns_cache_get(struct dns_cache *dns_cache)
|
||||
{
|
||||
if (atomic_inc_return(&dns_cache->ref) == 1) {
|
||||
tlog(TLOG_ERROR, "BUG: dns_cache is invalid.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void dns_cache_release(struct dns_cache *dns_cache)
|
||||
{
|
||||
if (dns_cache == NULL) {
|
||||
return;
|
||||
}
|
||||
if (!atomic_dec_and_test(&dns_cache->ref)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_dns_cache_delete(dns_cache);
|
||||
}
|
||||
|
||||
static void _dns_cache_remove(struct dns_cache *dns_cache)
|
||||
{
|
||||
hash_del(&dns_cache->node);
|
||||
list_del_init(&dns_cache->list);
|
||||
dns_cache_release(dns_cache);
|
||||
}
|
||||
|
||||
int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len, int speed)
|
||||
{
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
|
||||
if (dns_cache_head.size <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* lookup existing cache */
|
||||
dns_cache = dns_cache_lookup(domain, qtype);
|
||||
if (dns_cache == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ttl < DNS_CACHE_TTL_MIN) {
|
||||
ttl = DNS_CACHE_TTL_MIN;
|
||||
}
|
||||
|
||||
/* update cache data */
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
dns_cache->ttl = ttl;
|
||||
dns_cache->qtype = qtype;
|
||||
dns_cache->ttl = ttl;
|
||||
dns_cache->del_pending = 0;
|
||||
dns_cache->speed = speed;
|
||||
time(&dns_cache->insert_time);
|
||||
if (qtype == DNS_T_A) {
|
||||
if (addr_len != DNS_RR_A_LEN) {
|
||||
goto errout_unlock;
|
||||
}
|
||||
memcpy(dns_cache->addr, addr, DNS_RR_A_LEN);
|
||||
} else if (qtype == DNS_T_AAAA) {
|
||||
if (addr_len != DNS_RR_AAAA_LEN) {
|
||||
goto errout_unlock;
|
||||
}
|
||||
memcpy(dns_cache->addr, addr, DNS_RR_AAAA_LEN);
|
||||
} else {
|
||||
goto errout_unlock;
|
||||
}
|
||||
|
||||
if (cname) {
|
||||
safe_strncpy(dns_cache->cname, cname, DNS_MAX_CNAME_LEN);
|
||||
dns_cache->cname_ttl = cname_ttl;
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
dns_cache_release(dns_cache);
|
||||
return 0;
|
||||
errout_unlock:
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
// errout:
|
||||
if (dns_cache) {
|
||||
dns_cache_release(dns_cache);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len, int speed)
|
||||
{
|
||||
uint32_t key = 0;
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
|
||||
if (dns_cache_head.size <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if cache already exists, free */
|
||||
dns_cache = dns_cache_lookup(domain, qtype);
|
||||
if (dns_cache) {
|
||||
dns_cache_delete(dns_cache);
|
||||
dns_cache_release(dns_cache);
|
||||
dns_cache = NULL;
|
||||
}
|
||||
|
||||
dns_cache = malloc(sizeof(*dns_cache));
|
||||
if (dns_cache == NULL) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (ttl < DNS_CACHE_TTL_MIN) {
|
||||
ttl = DNS_CACHE_TTL_MIN;
|
||||
}
|
||||
|
||||
key = hash_string(domain);
|
||||
key = jhash(&qtype, sizeof(qtype), key);
|
||||
safe_strncpy(dns_cache->domain, domain, DNS_MAX_CNAME_LEN);
|
||||
dns_cache->cname[0] = 0;
|
||||
dns_cache->qtype = qtype;
|
||||
dns_cache->ttl = ttl;
|
||||
atomic_set(&dns_cache->hitnum, 3);
|
||||
dns_cache->hitnum_update_add = DNS_CACHE_HITNUM_STEP;
|
||||
dns_cache->del_pending = 0;
|
||||
dns_cache->speed = speed;
|
||||
atomic_set(&dns_cache->ref, 1);
|
||||
time(&dns_cache->insert_time);
|
||||
if (qtype == DNS_T_A) {
|
||||
if (addr_len != DNS_RR_A_LEN) {
|
||||
goto errout;
|
||||
}
|
||||
memcpy(dns_cache->addr, addr, DNS_RR_A_LEN);
|
||||
} else if (qtype == DNS_T_AAAA) {
|
||||
if (addr_len != DNS_RR_AAAA_LEN) {
|
||||
goto errout;
|
||||
}
|
||||
memcpy(dns_cache->addr, addr, DNS_RR_AAAA_LEN);
|
||||
} else {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (cname) {
|
||||
safe_strncpy(dns_cache->cname, cname, DNS_MAX_CNAME_LEN);
|
||||
dns_cache->cname_ttl = cname_ttl;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
hash_add(dns_cache_head.cache_hash, &dns_cache->node, key);
|
||||
list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
|
||||
INIT_LIST_HEAD(&dns_cache->check_list);
|
||||
|
||||
/* Release extra cache, remove oldest cache record */
|
||||
if (atomic_inc_return(&dns_cache_head.num) > dns_cache_head.size) {
|
||||
struct dns_cache *del_cache;
|
||||
del_cache = _dns_cache_first();
|
||||
if (del_cache) {
|
||||
_dns_cache_remove(del_cache);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
return 0;
|
||||
errout:
|
||||
if (dns_cache) {
|
||||
free(dns_cache);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype)
|
||||
{
|
||||
uint32_t key = 0;
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
struct dns_cache *dns_cache_ret = NULL;
|
||||
time_t now;
|
||||
|
||||
if (dns_cache_head.size <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
key = hash_string(domain);
|
||||
key = jhash(&qtype, sizeof(qtype), key);
|
||||
|
||||
time(&now);
|
||||
/* find cache */
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
hash_for_each_possible(dns_cache_head.cache_hash, dns_cache, node, key)
|
||||
{
|
||||
if (dns_cache->qtype != qtype) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp(domain, dns_cache->domain, DNS_MAX_CNAME_LEN) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dns_cache_ret = dns_cache;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dns_cache_ret) {
|
||||
/* Return NULL if the cache times out */
|
||||
if (now - dns_cache_ret->insert_time > dns_cache_ret->ttl) {
|
||||
_dns_cache_remove(dns_cache_ret);
|
||||
dns_cache_ret = NULL;
|
||||
} else {
|
||||
dns_cache_get(dns_cache_ret);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
return dns_cache_ret;
|
||||
}
|
||||
|
||||
int dns_cache_get_ttl(struct dns_cache *dns_cache)
|
||||
{
|
||||
time_t now;
|
||||
int ttl = 0;
|
||||
time(&now);
|
||||
|
||||
ttl = dns_cache->insert_time + dns_cache->ttl - now;
|
||||
if (ttl < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ttl;
|
||||
}
|
||||
|
||||
void dns_cache_delete(struct dns_cache *dns_cache)
|
||||
{
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
_dns_cache_remove(dns_cache);
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
}
|
||||
|
||||
int dns_cache_hitnum_dec_get(struct dns_cache *dns_cache)
|
||||
{
|
||||
int hitnum = 0;
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
hitnum = atomic_dec_return(&dns_cache->hitnum);
|
||||
if (dns_cache->hitnum_update_add > DNS_CACHE_HITNUM_STEP) {
|
||||
dns_cache->hitnum_update_add--;
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
return hitnum;
|
||||
}
|
||||
|
||||
void dns_cache_update(struct dns_cache *dns_cache)
|
||||
{
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
if (!list_empty(&dns_cache->list)) {
|
||||
list_del_init(&dns_cache->list);
|
||||
list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
|
||||
atomic_add(dns_cache->hitnum_update_add, &dns_cache->hitnum);
|
||||
if (atomic_read(&dns_cache->hitnum) > DNS_CACHE_MAX_HITNUM) {
|
||||
atomic_set(&dns_cache->hitnum, DNS_CACHE_MAX_HITNUM);
|
||||
}
|
||||
|
||||
if (dns_cache->hitnum_update_add < DNS_CACHE_HITNUM_STEP_MAX) {
|
||||
dns_cache->hitnum_update_add++;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
}
|
||||
|
||||
void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre)
|
||||
{
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
struct dns_cache *tmp;
|
||||
time_t now;
|
||||
int ttl = 0;
|
||||
LIST_HEAD(checklist);
|
||||
|
||||
if (dns_cache_head.size <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
time(&now);
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list)
|
||||
{
|
||||
ttl = dns_cache->insert_time + dns_cache->ttl - now;
|
||||
if (ttl > 0 && ttl < ttl_pre) {
|
||||
/* If the TTL time is in the pre-timeout range, call callback function */
|
||||
if (callback && dns_cache->del_pending == 0) {
|
||||
list_add_tail(&dns_cache->check_list, &checklist);
|
||||
dns_cache_get(dns_cache);
|
||||
dns_cache->del_pending = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (ttl < 0) {
|
||||
_dns_cache_remove(dns_cache);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
list_for_each_entry_safe(dns_cache, tmp, &checklist, check_list)
|
||||
{
|
||||
/* run callback */
|
||||
if (callback) {
|
||||
callback(dns_cache);
|
||||
}
|
||||
dns_cache_release(dns_cache);
|
||||
}
|
||||
}
|
||||
|
||||
void dns_cache_destroy(void)
|
||||
{
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
struct dns_cache *tmp;
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list)
|
||||
{
|
||||
_dns_cache_delete(dns_cache);
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
pthread_mutex_destroy(&dns_cache_head.lock);
|
||||
}
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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_cache.h"
|
||||
#include "stringutil.h"
|
||||
#include "tlog.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#define DNS_CACHE_MAX_HITNUM 5000
|
||||
#define DNS_CACHE_HITNUM_STEP 2
|
||||
#define DNS_CACHE_HITNUM_STEP_MAX 6
|
||||
|
||||
struct dns_cache_head {
|
||||
DECLARE_HASHTABLE(cache_hash, 10);
|
||||
struct list_head cache_list;
|
||||
atomic_t num;
|
||||
int size;
|
||||
pthread_mutex_t lock;
|
||||
};
|
||||
|
||||
static struct dns_cache_head dns_cache_head;
|
||||
|
||||
int dns_cache_init(int size)
|
||||
{
|
||||
INIT_LIST_HEAD(&dns_cache_head.cache_list);
|
||||
hash_init(dns_cache_head.cache_hash);
|
||||
atomic_set(&dns_cache_head.num, 0);
|
||||
dns_cache_head.size = size;
|
||||
|
||||
pthread_mutex_init(&dns_cache_head.lock, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) struct dns_cache *_dns_cache_last(void)
|
||||
{
|
||||
return list_last_entry(&dns_cache_head.cache_list, struct dns_cache, list);
|
||||
}
|
||||
|
||||
static struct dns_cache *_dns_cache_first(void)
|
||||
{
|
||||
return list_first_entry_or_null(&dns_cache_head.cache_list, struct dns_cache, list);
|
||||
}
|
||||
|
||||
static void _dns_cache_delete(struct dns_cache *dns_cache)
|
||||
{
|
||||
hash_del(&dns_cache->node);
|
||||
list_del_init(&dns_cache->list);
|
||||
atomic_dec(&dns_cache_head.num);
|
||||
free(dns_cache);
|
||||
}
|
||||
|
||||
void dns_cache_get(struct dns_cache *dns_cache)
|
||||
{
|
||||
if (atomic_inc_return(&dns_cache->ref) == 1) {
|
||||
tlog(TLOG_ERROR, "BUG: dns_cache is invalid.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void dns_cache_release(struct dns_cache *dns_cache)
|
||||
{
|
||||
if (dns_cache == NULL) {
|
||||
return;
|
||||
}
|
||||
if (!atomic_dec_and_test(&dns_cache->ref)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_dns_cache_delete(dns_cache);
|
||||
}
|
||||
|
||||
static void _dns_cache_remove(struct dns_cache *dns_cache)
|
||||
{
|
||||
hash_del(&dns_cache->node);
|
||||
list_del_init(&dns_cache->list);
|
||||
dns_cache_release(dns_cache);
|
||||
}
|
||||
|
||||
int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len, int speed)
|
||||
{
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
|
||||
if (dns_cache_head.size <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* lookup existing cache */
|
||||
dns_cache = dns_cache_lookup(domain, qtype);
|
||||
if (dns_cache == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ttl < DNS_CACHE_TTL_MIN) {
|
||||
ttl = DNS_CACHE_TTL_MIN;
|
||||
}
|
||||
|
||||
/* update cache data */
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
dns_cache->ttl = ttl;
|
||||
dns_cache->qtype = qtype;
|
||||
dns_cache->ttl = ttl;
|
||||
dns_cache->del_pending = 0;
|
||||
dns_cache->speed = speed;
|
||||
time(&dns_cache->insert_time);
|
||||
if (qtype == DNS_T_A) {
|
||||
if (addr_len != DNS_RR_A_LEN) {
|
||||
goto errout_unlock;
|
||||
}
|
||||
memcpy(dns_cache->addr, addr, DNS_RR_A_LEN);
|
||||
} else if (qtype == DNS_T_AAAA) {
|
||||
if (addr_len != DNS_RR_AAAA_LEN) {
|
||||
goto errout_unlock;
|
||||
}
|
||||
memcpy(dns_cache->addr, addr, DNS_RR_AAAA_LEN);
|
||||
} else {
|
||||
goto errout_unlock;
|
||||
}
|
||||
|
||||
if (cname) {
|
||||
safe_strncpy(dns_cache->cname, cname, DNS_MAX_CNAME_LEN);
|
||||
dns_cache->cname_ttl = cname_ttl;
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
dns_cache_release(dns_cache);
|
||||
return 0;
|
||||
errout_unlock:
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
// errout:
|
||||
if (dns_cache) {
|
||||
dns_cache_release(dns_cache);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len, int speed)
|
||||
{
|
||||
uint32_t key = 0;
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
|
||||
if (dns_cache_head.size <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if cache already exists, free */
|
||||
dns_cache = dns_cache_lookup(domain, qtype);
|
||||
if (dns_cache) {
|
||||
dns_cache_delete(dns_cache);
|
||||
dns_cache_release(dns_cache);
|
||||
dns_cache = NULL;
|
||||
}
|
||||
|
||||
dns_cache = malloc(sizeof(*dns_cache));
|
||||
if (dns_cache == NULL) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (ttl < DNS_CACHE_TTL_MIN) {
|
||||
ttl = DNS_CACHE_TTL_MIN;
|
||||
}
|
||||
|
||||
key = hash_string(domain);
|
||||
key = jhash(&qtype, sizeof(qtype), key);
|
||||
safe_strncpy(dns_cache->domain, domain, DNS_MAX_CNAME_LEN);
|
||||
dns_cache->cname[0] = 0;
|
||||
dns_cache->qtype = qtype;
|
||||
dns_cache->ttl = ttl;
|
||||
atomic_set(&dns_cache->hitnum, 3);
|
||||
dns_cache->hitnum_update_add = DNS_CACHE_HITNUM_STEP;
|
||||
dns_cache->del_pending = 0;
|
||||
dns_cache->speed = speed;
|
||||
atomic_set(&dns_cache->ref, 1);
|
||||
time(&dns_cache->insert_time);
|
||||
if (qtype == DNS_T_A) {
|
||||
if (addr_len != DNS_RR_A_LEN) {
|
||||
goto errout;
|
||||
}
|
||||
memcpy(dns_cache->addr, addr, DNS_RR_A_LEN);
|
||||
} else if (qtype == DNS_T_AAAA) {
|
||||
if (addr_len != DNS_RR_AAAA_LEN) {
|
||||
goto errout;
|
||||
}
|
||||
memcpy(dns_cache->addr, addr, DNS_RR_AAAA_LEN);
|
||||
} else {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (cname) {
|
||||
safe_strncpy(dns_cache->cname, cname, DNS_MAX_CNAME_LEN);
|
||||
dns_cache->cname_ttl = cname_ttl;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
hash_add(dns_cache_head.cache_hash, &dns_cache->node, key);
|
||||
list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
|
||||
INIT_LIST_HEAD(&dns_cache->check_list);
|
||||
|
||||
/* Release extra cache, remove oldest cache record */
|
||||
if (atomic_inc_return(&dns_cache_head.num) > dns_cache_head.size) {
|
||||
struct dns_cache *del_cache;
|
||||
del_cache = _dns_cache_first();
|
||||
if (del_cache) {
|
||||
_dns_cache_remove(del_cache);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
return 0;
|
||||
errout:
|
||||
if (dns_cache) {
|
||||
free(dns_cache);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype)
|
||||
{
|
||||
uint32_t key = 0;
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
struct dns_cache *dns_cache_ret = NULL;
|
||||
time_t now;
|
||||
|
||||
if (dns_cache_head.size <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
key = hash_string(domain);
|
||||
key = jhash(&qtype, sizeof(qtype), key);
|
||||
|
||||
time(&now);
|
||||
/* find cache */
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
hash_for_each_possible(dns_cache_head.cache_hash, dns_cache, node, key)
|
||||
{
|
||||
if (dns_cache->qtype != qtype) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp(domain, dns_cache->domain, DNS_MAX_CNAME_LEN) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dns_cache_ret = dns_cache;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dns_cache_ret) {
|
||||
/* Return NULL if the cache times out */
|
||||
if (now - dns_cache_ret->insert_time > dns_cache_ret->ttl) {
|
||||
_dns_cache_remove(dns_cache_ret);
|
||||
dns_cache_ret = NULL;
|
||||
} else {
|
||||
dns_cache_get(dns_cache_ret);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
return dns_cache_ret;
|
||||
}
|
||||
|
||||
int dns_cache_get_ttl(struct dns_cache *dns_cache)
|
||||
{
|
||||
time_t now;
|
||||
int ttl = 0;
|
||||
time(&now);
|
||||
|
||||
ttl = dns_cache->insert_time + dns_cache->ttl - now;
|
||||
if (ttl < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ttl;
|
||||
}
|
||||
|
||||
void dns_cache_delete(struct dns_cache *dns_cache)
|
||||
{
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
_dns_cache_remove(dns_cache);
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
}
|
||||
|
||||
int dns_cache_hitnum_dec_get(struct dns_cache *dns_cache)
|
||||
{
|
||||
int hitnum = 0;
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
hitnum = atomic_dec_return(&dns_cache->hitnum);
|
||||
if (dns_cache->hitnum_update_add > DNS_CACHE_HITNUM_STEP) {
|
||||
dns_cache->hitnum_update_add--;
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
return hitnum;
|
||||
}
|
||||
|
||||
void dns_cache_update(struct dns_cache *dns_cache)
|
||||
{
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
if (!list_empty(&dns_cache->list)) {
|
||||
list_del_init(&dns_cache->list);
|
||||
list_add_tail(&dns_cache->list, &dns_cache_head.cache_list);
|
||||
atomic_add(dns_cache->hitnum_update_add, &dns_cache->hitnum);
|
||||
if (atomic_read(&dns_cache->hitnum) > DNS_CACHE_MAX_HITNUM) {
|
||||
atomic_set(&dns_cache->hitnum, DNS_CACHE_MAX_HITNUM);
|
||||
}
|
||||
|
||||
if (dns_cache->hitnum_update_add < DNS_CACHE_HITNUM_STEP_MAX) {
|
||||
dns_cache->hitnum_update_add++;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
}
|
||||
|
||||
void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre)
|
||||
{
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
struct dns_cache *tmp;
|
||||
time_t now;
|
||||
int ttl = 0;
|
||||
LIST_HEAD(checklist);
|
||||
|
||||
if (dns_cache_head.size <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
time(&now);
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list)
|
||||
{
|
||||
ttl = dns_cache->insert_time + dns_cache->ttl - now;
|
||||
if (ttl > 0 && ttl < ttl_pre) {
|
||||
/* If the TTL time is in the pre-timeout range, call callback function */
|
||||
if (callback && dns_cache->del_pending == 0) {
|
||||
list_add_tail(&dns_cache->check_list, &checklist);
|
||||
dns_cache_get(dns_cache);
|
||||
dns_cache->del_pending = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (ttl < 0) {
|
||||
_dns_cache_remove(dns_cache);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
list_for_each_entry_safe(dns_cache, tmp, &checklist, check_list)
|
||||
{
|
||||
/* run callback */
|
||||
if (callback) {
|
||||
callback(dns_cache);
|
||||
}
|
||||
dns_cache_release(dns_cache);
|
||||
}
|
||||
}
|
||||
|
||||
void dns_cache_destroy(void)
|
||||
{
|
||||
struct dns_cache *dns_cache = NULL;
|
||||
struct dns_cache *tmp;
|
||||
pthread_mutex_lock(&dns_cache_head.lock);
|
||||
list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list)
|
||||
{
|
||||
_dns_cache_delete(dns_cache);
|
||||
}
|
||||
pthread_mutex_unlock(&dns_cache_head.lock);
|
||||
|
||||
pthread_mutex_destroy(&dns_cache_head.lock);
|
||||
}
|
||||
|
||||
174
src/dns_cache.h
174
src/dns_cache.h
@@ -1,87 +1,87 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _SMARTDNS_CACHE_H
|
||||
#define _SMARTDNS_CACHE_H
|
||||
|
||||
#include "atomic.h"
|
||||
#include "dns.h"
|
||||
#include "hash.h"
|
||||
#include "hashtable.h"
|
||||
#include "list.h"
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __cpluscplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DNS_CACHE_TTL_MIN 30
|
||||
|
||||
struct dns_cache {
|
||||
struct hlist_node node;
|
||||
struct list_head list;
|
||||
struct list_head check_list;
|
||||
atomic_t ref;
|
||||
char domain[DNS_MAX_CNAME_LEN];
|
||||
char cname[DNS_MAX_CNAME_LEN];
|
||||
unsigned int cname_ttl;
|
||||
unsigned int ttl;
|
||||
int speed;
|
||||
atomic_t hitnum;
|
||||
int hitnum_update_add;
|
||||
int del_pending;
|
||||
time_t insert_time;
|
||||
dns_type_t qtype;
|
||||
union {
|
||||
unsigned char ipv4_addr[DNS_RR_A_LEN];
|
||||
unsigned char ipv6_addr[DNS_RR_AAAA_LEN];
|
||||
unsigned char addr[0];
|
||||
};
|
||||
};
|
||||
|
||||
int dns_cache_init(int size);
|
||||
|
||||
int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len, int speed);
|
||||
|
||||
int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len, int speed);
|
||||
|
||||
struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype);
|
||||
|
||||
void dns_cache_delete(struct dns_cache *dns_cache);
|
||||
|
||||
void dns_cache_get(struct dns_cache *dns_cache);
|
||||
|
||||
void dns_cache_release(struct dns_cache *dns_cache);
|
||||
|
||||
int dns_cache_hitnum_dec_get(struct dns_cache *dns_cache);
|
||||
|
||||
void dns_cache_update(struct dns_cache *dns_cache);
|
||||
|
||||
typedef void dns_cache_preinvalid_callback(struct dns_cache *dns_cache);
|
||||
|
||||
void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre);
|
||||
|
||||
int dns_cache_get_ttl(struct dns_cache *dns_cache);
|
||||
|
||||
void dns_cache_destroy(void);
|
||||
|
||||
#ifdef __cpluscplus
|
||||
}
|
||||
#endif
|
||||
#endif // !_SMARTDNS_CACHE_H
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _SMARTDNS_CACHE_H
|
||||
#define _SMARTDNS_CACHE_H
|
||||
|
||||
#include "atomic.h"
|
||||
#include "dns.h"
|
||||
#include "hash.h"
|
||||
#include "hashtable.h"
|
||||
#include "list.h"
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __cpluscplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DNS_CACHE_TTL_MIN 30
|
||||
|
||||
struct dns_cache {
|
||||
struct hlist_node node;
|
||||
struct list_head list;
|
||||
struct list_head check_list;
|
||||
atomic_t ref;
|
||||
char domain[DNS_MAX_CNAME_LEN];
|
||||
char cname[DNS_MAX_CNAME_LEN];
|
||||
unsigned int cname_ttl;
|
||||
unsigned int ttl;
|
||||
int speed;
|
||||
atomic_t hitnum;
|
||||
int hitnum_update_add;
|
||||
int del_pending;
|
||||
time_t insert_time;
|
||||
dns_type_t qtype;
|
||||
union {
|
||||
unsigned char ipv4_addr[DNS_RR_A_LEN];
|
||||
unsigned char ipv6_addr[DNS_RR_AAAA_LEN];
|
||||
unsigned char addr[0];
|
||||
};
|
||||
};
|
||||
|
||||
int dns_cache_init(int size);
|
||||
|
||||
int dns_cache_replace(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len, int speed);
|
||||
|
||||
int dns_cache_insert(char *domain, char *cname, int cname_ttl, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len, int speed);
|
||||
|
||||
struct dns_cache *dns_cache_lookup(char *domain, dns_type_t qtype);
|
||||
|
||||
void dns_cache_delete(struct dns_cache *dns_cache);
|
||||
|
||||
void dns_cache_get(struct dns_cache *dns_cache);
|
||||
|
||||
void dns_cache_release(struct dns_cache *dns_cache);
|
||||
|
||||
int dns_cache_hitnum_dec_get(struct dns_cache *dns_cache);
|
||||
|
||||
void dns_cache_update(struct dns_cache *dns_cache);
|
||||
|
||||
typedef void dns_cache_preinvalid_callback(struct dns_cache *dns_cache);
|
||||
|
||||
void dns_cache_invalidate(dns_cache_preinvalid_callback callback, int ttl_pre);
|
||||
|
||||
int dns_cache_get_ttl(struct dns_cache *dns_cache);
|
||||
|
||||
void dns_cache_destroy(void);
|
||||
|
||||
#ifdef __cpluscplus
|
||||
}
|
||||
#endif
|
||||
#endif // !_SMARTDNS_CACHE_H
|
||||
|
||||
2724
src/dns_conf.c
2724
src/dns_conf.c
File diff suppressed because it is too large
Load Diff
494
src/dns_conf.h
494
src/dns_conf.h
@@ -1,247 +1,247 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _DNS_CONF
|
||||
#define _DNS_CONF
|
||||
|
||||
#include "art.h"
|
||||
#include "conf.h"
|
||||
#include "dns.h"
|
||||
#include "dns_client.h"
|
||||
#include "hash.h"
|
||||
#include "hashtable.h"
|
||||
#include "list.h"
|
||||
#include "radix.h"
|
||||
|
||||
#ifdef __cpluscplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DNS_MAX_BIND_IP 16
|
||||
#define DNS_MAX_SERVERS 64
|
||||
#define DNS_MAX_SERVER_NAME_LEN 128
|
||||
#define DNS_MAX_IPSET_NAMELEN 32
|
||||
#define DNS_GROUP_NAME_LEN 32
|
||||
#define DNS_NAX_GROUP_NUMBER 16
|
||||
#define DNS_MAX_IPLEN 64
|
||||
#define DNS_MAX_SPKI_LEN 64
|
||||
#define DNS_MAX_URL_LEN 256
|
||||
#define DNS_MAX_PATH 1024
|
||||
#define DEFAULT_DNS_PORT 53
|
||||
#define DEFAULT_DNS_TLS_PORT 853
|
||||
#define DEFAULT_DNS_HTTPS_PORT 443
|
||||
#define DNS_MAX_CONF_CNAME_LEN 256
|
||||
#define SMARTDNS_CONF_FILE "/etc/smartdns/smartdns.conf"
|
||||
#define SMARTDNS_LOG_FILE "/var/log/smartdns.log"
|
||||
#define SMARTDNS_AUDIT_FILE "/var/log/smartdns-audit.log"
|
||||
|
||||
enum domain_rule {
|
||||
DOMAIN_RULE_FLAGS = 0,
|
||||
DOMAIN_RULE_ADDRESS_IPV4,
|
||||
DOMAIN_RULE_ADDRESS_IPV6,
|
||||
DOMAIN_RULE_IPSET,
|
||||
DOMAIN_RULE_NAMESERVER,
|
||||
DOMAIN_RULE_MAX,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
DNS_BIND_TYPE_UDP,
|
||||
DNS_BIND_TYPE_TCP,
|
||||
DNS_BIND_TYPE_TLS,
|
||||
} DNS_BIND_TYPE;
|
||||
|
||||
#define DOMAIN_CHECK_NONE 0
|
||||
#define DOMAIN_CHECK_ICMP 1
|
||||
#define DOMAIN_CHECK_TCP 2
|
||||
#define DOMAIN_CHECK_NUM 2
|
||||
|
||||
#define DOMAIN_FLAG_ADDR_SOA (1 << 0)
|
||||
#define DOMAIN_FLAG_ADDR_IPV4_SOA (1 << 1)
|
||||
#define DOMAIN_FLAG_ADDR_IPV6_SOA (1 << 2)
|
||||
#define DOMAIN_FLAG_ADDR_IGN (1 << 3)
|
||||
#define DOMAIN_FLAG_ADDR_IPV4_IGN (1 << 4)
|
||||
#define DOMAIN_FLAG_ADDR_IPV6_IGN (1 << 5)
|
||||
#define DOMAIN_FLAG_IPSET_IGNORE (1 << 6)
|
||||
#define DOMAIN_FLAG_NAMESERVER_IGNORE (1 << 7)
|
||||
|
||||
#define SERVER_FLAG_EXCLUDE_DEFAULT (1 << 0)
|
||||
|
||||
#define BIND_FLAG_NO_RULE_ADDR (1 << 0)
|
||||
#define BIND_FLAG_NO_RULE_NAMESERVER (1 << 1)
|
||||
#define BIND_FLAG_NO_RULE_IPSET (1 << 2)
|
||||
#define BIND_FLAG_NO_RULE_SNIPROXY (1 << 3)
|
||||
#define BIND_FLAG_NO_RULE_SOA (1 << 4)
|
||||
#define BIND_FLAG_NO_SPEED_CHECK (1 << 5)
|
||||
#define BIND_FLAG_NO_CACHE (1 << 6)
|
||||
#define BIND_FLAG_NO_DUALSTACK_SELECTION (1 << 7)
|
||||
|
||||
struct dns_rule_flags {
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
struct dns_address_IPV4 {
|
||||
unsigned char ipv4_addr[DNS_RR_A_LEN];
|
||||
};
|
||||
|
||||
struct dns_address_IPV6 {
|
||||
unsigned char ipv6_addr[DNS_RR_AAAA_LEN];
|
||||
};
|
||||
|
||||
struct dns_ipset_name {
|
||||
struct hlist_node node;
|
||||
char ipsetname[DNS_MAX_IPSET_NAMELEN];
|
||||
};
|
||||
|
||||
struct dns_ipset_rule {
|
||||
const char *ipsetname;
|
||||
};
|
||||
|
||||
struct dns_domain_rule {
|
||||
void *rules[DOMAIN_RULE_MAX];
|
||||
};
|
||||
|
||||
struct dns_nameserver_rule {
|
||||
const char *group_name;
|
||||
};
|
||||
|
||||
struct dns_server_groups {
|
||||
struct hlist_node node;
|
||||
char group_name[DNS_GROUP_NAME_LEN];
|
||||
int server_num;
|
||||
struct dns_servers *servers[DNS_MAX_SERVERS];
|
||||
};
|
||||
|
||||
struct dns_domain_check_order {
|
||||
char order[DOMAIN_CHECK_NUM];
|
||||
unsigned short tcp_port;
|
||||
};
|
||||
|
||||
struct dns_group_table {
|
||||
DECLARE_HASHTABLE(group, 8);
|
||||
};
|
||||
extern struct dns_group_table dns_group_table;
|
||||
|
||||
struct dns_servers {
|
||||
char server[DNS_MAX_IPLEN];
|
||||
unsigned short port;
|
||||
unsigned int result_flag;
|
||||
unsigned int server_flag;
|
||||
int ttl;
|
||||
dns_server_type_t type;
|
||||
char spki[DNS_MAX_SPKI_LEN];
|
||||
char hostname[DNS_MAX_CNAME_LEN];
|
||||
char httphost[DNS_MAX_CNAME_LEN];
|
||||
char tls_host_verify[DNS_MAX_CNAME_LEN];
|
||||
char path[DNS_MAX_URL_LEN];
|
||||
};
|
||||
|
||||
/* ip address lists of domain */
|
||||
struct dns_bogus_ip_address {
|
||||
struct hlist_node node;
|
||||
dns_type_t addr_type;
|
||||
union {
|
||||
unsigned char ipv4_addr[DNS_RR_A_LEN];
|
||||
unsigned char ipv6_addr[DNS_RR_AAAA_LEN];
|
||||
unsigned char addr[0];
|
||||
};
|
||||
};
|
||||
|
||||
enum address_rule {
|
||||
ADDRESS_RULE_BLACKLIST = 1,
|
||||
ADDRESS_RULE_WHITELIST = 2,
|
||||
ADDRESS_RULE_BOGUS = 3,
|
||||
ADDRESS_RULE_IP_IGNORE = 4,
|
||||
};
|
||||
|
||||
struct dns_ip_address_rule {
|
||||
unsigned int blacklist : 1;
|
||||
unsigned int whitelist : 1;
|
||||
unsigned int bogus : 1;
|
||||
unsigned int ip_ignore : 1;
|
||||
};
|
||||
|
||||
struct dns_edns_client_subnet {
|
||||
int enable;
|
||||
char ip[DNS_MAX_IPLEN];
|
||||
int subnet;
|
||||
};
|
||||
|
||||
struct dns_conf_address_rule {
|
||||
radix_tree_t *ipv4;
|
||||
radix_tree_t *ipv6;
|
||||
};
|
||||
|
||||
struct dns_bind_ip {
|
||||
DNS_BIND_TYPE type;
|
||||
uint32_t flags;
|
||||
char ip[DNS_MAX_IPLEN];
|
||||
const char *group;
|
||||
};
|
||||
|
||||
extern struct dns_bind_ip dns_conf_bind_ip[DNS_MAX_BIND_IP];
|
||||
extern int dns_conf_bind_ip_num;
|
||||
|
||||
extern int dns_conf_tcp_idle_time;
|
||||
extern int dns_conf_cachesize;
|
||||
extern int dns_conf_prefetch;
|
||||
extern struct dns_servers dns_conf_servers[DNS_MAX_SERVERS];
|
||||
extern int dns_conf_server_num;
|
||||
|
||||
extern int dns_conf_log_level;
|
||||
extern char dns_conf_log_file[DNS_MAX_PATH];
|
||||
extern size_t dns_conf_log_size;
|
||||
extern int dns_conf_log_num;
|
||||
|
||||
extern struct dns_domain_check_order dns_conf_check_order;
|
||||
|
||||
extern struct dns_server_groups dns_conf_server_groups[DNS_NAX_GROUP_NUMBER];
|
||||
extern int dns_conf_server_group_num;
|
||||
|
||||
extern int dns_conf_audit_enable;
|
||||
extern int dns_conf_audit_log_SOA;
|
||||
extern char dns_conf_audit_file[DNS_MAX_PATH];
|
||||
extern size_t dns_conf_audit_size;
|
||||
extern int dns_conf_audit_num;
|
||||
|
||||
extern char dns_conf_server_name[DNS_MAX_SERVER_NAME_LEN];
|
||||
extern art_tree dns_conf_domain_rule;
|
||||
extern struct dns_conf_address_rule dns_conf_address_rule;
|
||||
|
||||
extern int dns_conf_dualstack_ip_selection;
|
||||
extern int dns_conf_dualstack_ip_selection_threshold;
|
||||
|
||||
extern int dns_conf_rr_ttl;
|
||||
extern int dns_conf_rr_ttl_min;
|
||||
extern int dns_conf_rr_ttl_max;
|
||||
extern int dns_conf_force_AAAA_SOA;
|
||||
extern int dns_conf_ipset_timeout_enable;
|
||||
|
||||
extern struct dns_edns_client_subnet dns_conf_ipv4_ecs;
|
||||
extern struct dns_edns_client_subnet dns_conf_ipv6_ecs;
|
||||
|
||||
extern char dns_conf_sni_proxy_ip[DNS_MAX_IPLEN];
|
||||
|
||||
void dns_server_load_exit(void);
|
||||
|
||||
int dns_server_load_conf(const char *file);
|
||||
|
||||
extern int config_addtional_file(void *data, int argc, char *argv[]);
|
||||
#ifdef __cpluscplus
|
||||
}
|
||||
#endif
|
||||
#endif // !_DNS_CONF
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _DNS_CONF
|
||||
#define _DNS_CONF
|
||||
|
||||
#include "art.h"
|
||||
#include "conf.h"
|
||||
#include "dns.h"
|
||||
#include "dns_client.h"
|
||||
#include "hash.h"
|
||||
#include "hashtable.h"
|
||||
#include "list.h"
|
||||
#include "radix.h"
|
||||
|
||||
#ifdef __cpluscplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DNS_MAX_BIND_IP 16
|
||||
#define DNS_MAX_SERVERS 64
|
||||
#define DNS_MAX_SERVER_NAME_LEN 128
|
||||
#define DNS_MAX_IPSET_NAMELEN 32
|
||||
#define DNS_GROUP_NAME_LEN 32
|
||||
#define DNS_NAX_GROUP_NUMBER 16
|
||||
#define DNS_MAX_IPLEN 64
|
||||
#define DNS_MAX_SPKI_LEN 64
|
||||
#define DNS_MAX_URL_LEN 256
|
||||
#define DNS_MAX_PATH 1024
|
||||
#define DEFAULT_DNS_PORT 53
|
||||
#define DEFAULT_DNS_TLS_PORT 853
|
||||
#define DEFAULT_DNS_HTTPS_PORT 443
|
||||
#define DNS_MAX_CONF_CNAME_LEN 256
|
||||
#define SMARTDNS_CONF_FILE "/etc/smartdns/smartdns.conf"
|
||||
#define SMARTDNS_LOG_FILE "/var/log/smartdns.log"
|
||||
#define SMARTDNS_AUDIT_FILE "/var/log/smartdns-audit.log"
|
||||
|
||||
enum domain_rule {
|
||||
DOMAIN_RULE_FLAGS = 0,
|
||||
DOMAIN_RULE_ADDRESS_IPV4,
|
||||
DOMAIN_RULE_ADDRESS_IPV6,
|
||||
DOMAIN_RULE_IPSET,
|
||||
DOMAIN_RULE_NAMESERVER,
|
||||
DOMAIN_RULE_MAX,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
DNS_BIND_TYPE_UDP,
|
||||
DNS_BIND_TYPE_TCP,
|
||||
DNS_BIND_TYPE_TLS,
|
||||
} DNS_BIND_TYPE;
|
||||
|
||||
#define DOMAIN_CHECK_NONE 0
|
||||
#define DOMAIN_CHECK_ICMP 1
|
||||
#define DOMAIN_CHECK_TCP 2
|
||||
#define DOMAIN_CHECK_NUM 2
|
||||
|
||||
#define DOMAIN_FLAG_ADDR_SOA (1 << 0)
|
||||
#define DOMAIN_FLAG_ADDR_IPV4_SOA (1 << 1)
|
||||
#define DOMAIN_FLAG_ADDR_IPV6_SOA (1 << 2)
|
||||
#define DOMAIN_FLAG_ADDR_IGN (1 << 3)
|
||||
#define DOMAIN_FLAG_ADDR_IPV4_IGN (1 << 4)
|
||||
#define DOMAIN_FLAG_ADDR_IPV6_IGN (1 << 5)
|
||||
#define DOMAIN_FLAG_IPSET_IGNORE (1 << 6)
|
||||
#define DOMAIN_FLAG_NAMESERVER_IGNORE (1 << 7)
|
||||
|
||||
#define SERVER_FLAG_EXCLUDE_DEFAULT (1 << 0)
|
||||
|
||||
#define BIND_FLAG_NO_RULE_ADDR (1 << 0)
|
||||
#define BIND_FLAG_NO_RULE_NAMESERVER (1 << 1)
|
||||
#define BIND_FLAG_NO_RULE_IPSET (1 << 2)
|
||||
#define BIND_FLAG_NO_RULE_SNIPROXY (1 << 3)
|
||||
#define BIND_FLAG_NO_RULE_SOA (1 << 4)
|
||||
#define BIND_FLAG_NO_SPEED_CHECK (1 << 5)
|
||||
#define BIND_FLAG_NO_CACHE (1 << 6)
|
||||
#define BIND_FLAG_NO_DUALSTACK_SELECTION (1 << 7)
|
||||
|
||||
struct dns_rule_flags {
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
struct dns_address_IPV4 {
|
||||
unsigned char ipv4_addr[DNS_RR_A_LEN];
|
||||
};
|
||||
|
||||
struct dns_address_IPV6 {
|
||||
unsigned char ipv6_addr[DNS_RR_AAAA_LEN];
|
||||
};
|
||||
|
||||
struct dns_ipset_name {
|
||||
struct hlist_node node;
|
||||
char ipsetname[DNS_MAX_IPSET_NAMELEN];
|
||||
};
|
||||
|
||||
struct dns_ipset_rule {
|
||||
const char *ipsetname;
|
||||
};
|
||||
|
||||
struct dns_domain_rule {
|
||||
void *rules[DOMAIN_RULE_MAX];
|
||||
};
|
||||
|
||||
struct dns_nameserver_rule {
|
||||
const char *group_name;
|
||||
};
|
||||
|
||||
struct dns_server_groups {
|
||||
struct hlist_node node;
|
||||
char group_name[DNS_GROUP_NAME_LEN];
|
||||
int server_num;
|
||||
struct dns_servers *servers[DNS_MAX_SERVERS];
|
||||
};
|
||||
|
||||
struct dns_domain_check_order {
|
||||
char order[DOMAIN_CHECK_NUM];
|
||||
unsigned short tcp_port;
|
||||
};
|
||||
|
||||
struct dns_group_table {
|
||||
DECLARE_HASHTABLE(group, 8);
|
||||
};
|
||||
extern struct dns_group_table dns_group_table;
|
||||
|
||||
struct dns_servers {
|
||||
char server[DNS_MAX_IPLEN];
|
||||
unsigned short port;
|
||||
unsigned int result_flag;
|
||||
unsigned int server_flag;
|
||||
int ttl;
|
||||
dns_server_type_t type;
|
||||
char spki[DNS_MAX_SPKI_LEN];
|
||||
char hostname[DNS_MAX_CNAME_LEN];
|
||||
char httphost[DNS_MAX_CNAME_LEN];
|
||||
char tls_host_verify[DNS_MAX_CNAME_LEN];
|
||||
char path[DNS_MAX_URL_LEN];
|
||||
};
|
||||
|
||||
/* ip address lists of domain */
|
||||
struct dns_bogus_ip_address {
|
||||
struct hlist_node node;
|
||||
dns_type_t addr_type;
|
||||
union {
|
||||
unsigned char ipv4_addr[DNS_RR_A_LEN];
|
||||
unsigned char ipv6_addr[DNS_RR_AAAA_LEN];
|
||||
unsigned char addr[0];
|
||||
};
|
||||
};
|
||||
|
||||
enum address_rule {
|
||||
ADDRESS_RULE_BLACKLIST = 1,
|
||||
ADDRESS_RULE_WHITELIST = 2,
|
||||
ADDRESS_RULE_BOGUS = 3,
|
||||
ADDRESS_RULE_IP_IGNORE = 4,
|
||||
};
|
||||
|
||||
struct dns_ip_address_rule {
|
||||
unsigned int blacklist : 1;
|
||||
unsigned int whitelist : 1;
|
||||
unsigned int bogus : 1;
|
||||
unsigned int ip_ignore : 1;
|
||||
};
|
||||
|
||||
struct dns_edns_client_subnet {
|
||||
int enable;
|
||||
char ip[DNS_MAX_IPLEN];
|
||||
int subnet;
|
||||
};
|
||||
|
||||
struct dns_conf_address_rule {
|
||||
radix_tree_t *ipv4;
|
||||
radix_tree_t *ipv6;
|
||||
};
|
||||
|
||||
struct dns_bind_ip {
|
||||
DNS_BIND_TYPE type;
|
||||
uint32_t flags;
|
||||
char ip[DNS_MAX_IPLEN];
|
||||
const char *group;
|
||||
};
|
||||
|
||||
extern struct dns_bind_ip dns_conf_bind_ip[DNS_MAX_BIND_IP];
|
||||
extern int dns_conf_bind_ip_num;
|
||||
|
||||
extern int dns_conf_tcp_idle_time;
|
||||
extern int dns_conf_cachesize;
|
||||
extern int dns_conf_prefetch;
|
||||
extern struct dns_servers dns_conf_servers[DNS_MAX_SERVERS];
|
||||
extern int dns_conf_server_num;
|
||||
|
||||
extern int dns_conf_log_level;
|
||||
extern char dns_conf_log_file[DNS_MAX_PATH];
|
||||
extern size_t dns_conf_log_size;
|
||||
extern int dns_conf_log_num;
|
||||
|
||||
extern struct dns_domain_check_order dns_conf_check_order;
|
||||
|
||||
extern struct dns_server_groups dns_conf_server_groups[DNS_NAX_GROUP_NUMBER];
|
||||
extern int dns_conf_server_group_num;
|
||||
|
||||
extern int dns_conf_audit_enable;
|
||||
extern int dns_conf_audit_log_SOA;
|
||||
extern char dns_conf_audit_file[DNS_MAX_PATH];
|
||||
extern size_t dns_conf_audit_size;
|
||||
extern int dns_conf_audit_num;
|
||||
|
||||
extern char dns_conf_server_name[DNS_MAX_SERVER_NAME_LEN];
|
||||
extern art_tree dns_conf_domain_rule;
|
||||
extern struct dns_conf_address_rule dns_conf_address_rule;
|
||||
|
||||
extern int dns_conf_dualstack_ip_selection;
|
||||
extern int dns_conf_dualstack_ip_selection_threshold;
|
||||
|
||||
extern int dns_conf_rr_ttl;
|
||||
extern int dns_conf_rr_ttl_min;
|
||||
extern int dns_conf_rr_ttl_max;
|
||||
extern int dns_conf_force_AAAA_SOA;
|
||||
extern int dns_conf_ipset_timeout_enable;
|
||||
|
||||
extern struct dns_edns_client_subnet dns_conf_ipv4_ecs;
|
||||
extern struct dns_edns_client_subnet dns_conf_ipv6_ecs;
|
||||
|
||||
extern char dns_conf_sni_proxy_ip[DNS_MAX_IPLEN];
|
||||
|
||||
void dns_server_load_exit(void);
|
||||
|
||||
int dns_server_load_conf(const char *file);
|
||||
|
||||
extern int config_addtional_file(void *data, int argc, char *argv[]);
|
||||
#ifdef __cpluscplus
|
||||
}
|
||||
#endif
|
||||
#endif // !_DNS_CONF
|
||||
|
||||
3368
src/fast_ping.c
3368
src/fast_ping.c
File diff suppressed because it is too large
Load Diff
116
src/fast_ping.h
116
src/fast_ping.h
@@ -1,58 +1,58 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#ifndef FAST_PING_H
|
||||
#define FAST_PING_H
|
||||
|
||||
#include <netdb.h>
|
||||
#include <sys/time.h>
|
||||
#ifdef __cpluscplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
PING_TYPE_ICMP = 1,
|
||||
PING_TYPE_TCP = 2,
|
||||
PING_TYPE_DNS = 3,
|
||||
} 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, int ttl, struct timeval *tv, void *userptr);
|
||||
|
||||
/* start ping */
|
||||
struct ping_host_struct *fast_ping_start(PING_TYPE type, 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
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#ifndef FAST_PING_H
|
||||
#define FAST_PING_H
|
||||
|
||||
#include <netdb.h>
|
||||
#include <sys/time.h>
|
||||
#ifdef __cpluscplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
PING_TYPE_ICMP = 1,
|
||||
PING_TYPE_TCP = 2,
|
||||
PING_TYPE_DNS = 3,
|
||||
} 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, int ttl, struct timeval *tv, void *userptr);
|
||||
|
||||
/* start ping */
|
||||
struct ping_host_struct *fast_ping_start(PING_TYPE type, 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
|
||||
|
||||
926
src/http_parse.c
926
src/http_parse.c
@@ -1,463 +1,463 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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 "http_parse.h"
|
||||
#include "hash.h"
|
||||
#include "hashtable.h"
|
||||
#include "jhash.h"
|
||||
#include "list.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct http_head_fields {
|
||||
struct hlist_node node;
|
||||
struct list_head list;
|
||||
|
||||
char *name;
|
||||
char *value;
|
||||
};
|
||||
|
||||
struct http_head {
|
||||
HTTP_HEAD_TYPE head_type;
|
||||
HTTP_METHOD method;
|
||||
char *url;
|
||||
char *version;
|
||||
int code;
|
||||
char *code_msg;
|
||||
int buff_size;
|
||||
int buff_len;
|
||||
char *buff;
|
||||
int head_ok;
|
||||
int head_len;
|
||||
char *data;
|
||||
int data_len;
|
||||
int expect_data_len;
|
||||
struct http_head_fields field_head;
|
||||
DECLARE_HASHTABLE(field_map, 4);
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns:
|
||||
* >=0 - success http data len
|
||||
* -1 - Incomplete request
|
||||
* -2 - parse failed
|
||||
*/
|
||||
struct http_head *http_head_init(int buffsize)
|
||||
{
|
||||
struct http_head *http_head = NULL;
|
||||
char *buffer = NULL;
|
||||
|
||||
http_head = malloc(sizeof(*http_head));
|
||||
if (http_head == NULL) {
|
||||
goto errout;
|
||||
}
|
||||
memset(http_head, 0, sizeof(*http_head));
|
||||
INIT_LIST_HEAD(&http_head->field_head.list);
|
||||
hash_init(http_head->field_map);
|
||||
|
||||
buffer = malloc(buffsize);
|
||||
if (buffer == NULL) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
http_head->buff = buffer;
|
||||
http_head->buff_size = buffsize;
|
||||
|
||||
return http_head;
|
||||
|
||||
errout:
|
||||
if (buffer) {
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
if (http_head) {
|
||||
free(http_head);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct http_head_fields *http_head_first_fields(struct http_head *http_head)
|
||||
{
|
||||
struct http_head_fields *first = NULL;
|
||||
first = list_first_entry(&http_head->field_head.list, struct http_head_fields, list);
|
||||
|
||||
if (first->name == NULL && first->value == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
const char *http_head_get_fields_value(struct http_head *http_head, const char *name)
|
||||
{
|
||||
unsigned long key;
|
||||
struct http_head_fields *filed;
|
||||
|
||||
key = hash_string(name);
|
||||
hash_for_each_possible(http_head->field_map, filed, node, key)
|
||||
{
|
||||
if (strncmp(filed->name, name, 128) == 0) {
|
||||
return filed->value;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct http_head_fields *http_head_next_fields(struct http_head_fields *fields)
|
||||
{
|
||||
struct http_head_fields *next = NULL;
|
||||
next = list_next_entry(fields, list);
|
||||
|
||||
if (next->name == NULL && next->value == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
int http_head_lookup_fields(struct http_head_fields *fields, const char **name, const char **value)
|
||||
{
|
||||
if (fields == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
*name = fields->name;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
*value = fields->value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
HTTP_METHOD http_head_get_method(struct http_head *http_head)
|
||||
{
|
||||
return http_head->method;
|
||||
}
|
||||
|
||||
const char *http_head_get_url(struct http_head *http_head)
|
||||
{
|
||||
return http_head->url;
|
||||
}
|
||||
|
||||
const char *http_head_get_httpversion(struct http_head *http_head)
|
||||
{
|
||||
return http_head->version;
|
||||
}
|
||||
|
||||
int http_head_get_httpcode(struct http_head *http_head)
|
||||
{
|
||||
return http_head->code;
|
||||
}
|
||||
|
||||
char *http_head_get_httpcode_msg(struct http_head *http_head)
|
||||
{
|
||||
return http_head->code_msg;
|
||||
}
|
||||
|
||||
HTTP_HEAD_TYPE http_head_get_head_type(struct http_head *http_head)
|
||||
{
|
||||
return http_head->head_type;
|
||||
}
|
||||
|
||||
char *http_head_get_data(struct http_head *http_head)
|
||||
{
|
||||
return http_head->data;
|
||||
}
|
||||
|
||||
int http_head_get_data_len(struct http_head *http_head)
|
||||
{
|
||||
return http_head->data_len;
|
||||
}
|
||||
|
||||
static int _http_head_add_fields(struct http_head *http_head, char *name, char *value)
|
||||
{
|
||||
unsigned long key = 0;
|
||||
struct http_head_fields *fields = NULL;
|
||||
fields = malloc(sizeof(*fields));
|
||||
if (fields == NULL) {
|
||||
return -1;
|
||||
}
|
||||
memset(fields, 0, sizeof(*fields));
|
||||
|
||||
fields->name = name;
|
||||
fields->value = value;
|
||||
|
||||
list_add_tail(&fields->list, &http_head->field_head.list);
|
||||
key = hash_string(name);
|
||||
hash_add(http_head->field_map, &fields->node, key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _http_head_parse_response(struct http_head *http_head, char *key, char *value)
|
||||
{
|
||||
char *field_start = NULL;
|
||||
char *tmp_ptr = NULL;
|
||||
char *result = NULL;
|
||||
char *ret_code = NULL;
|
||||
|
||||
if (strstr(key, "HTTP/") == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (tmp_ptr = value; *tmp_ptr != 0; tmp_ptr++) {
|
||||
if (field_start == NULL) {
|
||||
field_start = tmp_ptr;
|
||||
}
|
||||
|
||||
if (*tmp_ptr == ' ') {
|
||||
*tmp_ptr = '\0';
|
||||
if (ret_code == NULL) {
|
||||
ret_code = field_start;
|
||||
} else if (result == NULL) {
|
||||
result = field_start;
|
||||
break;
|
||||
}
|
||||
|
||||
field_start = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (field_start && result == NULL) {
|
||||
result = field_start;
|
||||
}
|
||||
|
||||
if (ret_code == NULL || result == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
http_head->code = atol(ret_code);
|
||||
http_head->code_msg = result;
|
||||
http_head->version = key;
|
||||
http_head->head_type = HTTP_HEAD_RESPONSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _http_head_parse_request(struct http_head *http_head, char *key, char *value)
|
||||
{
|
||||
int method = HTTP_METHOD_INVALID;
|
||||
char *url = NULL;
|
||||
char *version = NULL;
|
||||
char *tmp_ptr = value;
|
||||
char *field_start = NULL;
|
||||
|
||||
if (strncmp(key, "GET", sizeof("GET")) == 0) {
|
||||
method = HTTP_METHOD_GET;
|
||||
} else if (strncmp(key, "POST", sizeof("POST")) == 0) {
|
||||
method = HTTP_METHOD_POST;
|
||||
} else if (strncmp(key, "PUT", sizeof("PUT")) == 0) {
|
||||
method = HTTP_METHOD_PUT;
|
||||
} else if (strncmp(key, "DELETE", sizeof("DELETE")) == 0) {
|
||||
method = HTTP_METHOD_DELETE;
|
||||
} else if (strncmp(key, "TRACE", sizeof("TRACE")) == 0) {
|
||||
method = HTTP_METHOD_TRACE;
|
||||
} else if (strncmp(key, "CONNECT", sizeof("CONNECT")) == 0) {
|
||||
method = HTTP_METHOD_CONNECT;
|
||||
} else {
|
||||
return _http_head_parse_response(http_head, key, value);
|
||||
}
|
||||
|
||||
for (tmp_ptr = value; *tmp_ptr != 0; tmp_ptr++) {
|
||||
if (field_start == NULL) {
|
||||
field_start = tmp_ptr;
|
||||
}
|
||||
if (*tmp_ptr == ' ') {
|
||||
*tmp_ptr = '\0';
|
||||
if (url == NULL) {
|
||||
url = field_start;
|
||||
}
|
||||
|
||||
field_start = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (field_start && version == NULL) {
|
||||
version = field_start;
|
||||
}
|
||||
|
||||
http_head->method = method;
|
||||
http_head->url = url;
|
||||
http_head->version = version;
|
||||
http_head->head_type = HTTP_HEAD_REQUEST;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _http_head_parse(struct http_head *http_head)
|
||||
{
|
||||
int i = 0;
|
||||
char *key = NULL;
|
||||
char *value = NULL;
|
||||
char *data;
|
||||
int has_first_line = 0;
|
||||
|
||||
int inkey = 1;
|
||||
int invalue = 0;
|
||||
|
||||
data = http_head->buff;
|
||||
for (i = 0; i < http_head->head_len; i++, data++) {
|
||||
if (inkey) {
|
||||
if (key == NULL && *data != ' ' && *data != '\r' && *data != '\n') {
|
||||
key = data;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*data == ':' || *data == ' ') {
|
||||
*data = '\0';
|
||||
inkey = 0;
|
||||
invalue = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalue) {
|
||||
if (value == NULL && *data != ' ') {
|
||||
value = data;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*data == '\r' || *data == '\n') {
|
||||
*data = '\0';
|
||||
inkey = 1;
|
||||
invalue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (key && value && invalue == 0) {
|
||||
if (has_first_line == 0) {
|
||||
if (_http_head_parse_request(http_head, key, value) != 0) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
has_first_line = 1;
|
||||
} else {
|
||||
if (_http_head_add_fields(http_head, key, value) != 0) {
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
key = NULL;
|
||||
value = NULL;
|
||||
inkey = 1;
|
||||
invalue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_head_parse(struct http_head *http_head, const char *data, int data_len)
|
||||
{
|
||||
int i = 0;
|
||||
char *buff_end = NULL;
|
||||
int left_size = 0;
|
||||
int process_data_len = 0;
|
||||
|
||||
left_size = http_head->buff_size - http_head->buff_len;
|
||||
|
||||
if (left_size < data_len) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
buff_end = http_head->buff + http_head->buff_len;
|
||||
if (http_head->head_ok == 0) {
|
||||
for (i = 0; i < data_len; i++, data++) {
|
||||
*(buff_end + i) = *data;
|
||||
if (*data == '\n') {
|
||||
if (http_head->buff_len + i < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*(buff_end + i - 2) == '\n') {
|
||||
http_head->head_ok = 1;
|
||||
http_head->head_len = http_head->buff_len + i - 2;
|
||||
i++;
|
||||
buff_end += i;
|
||||
data_len -= i;
|
||||
data++;
|
||||
if (_http_head_parse(http_head) != 0) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
const char *content_len = NULL;
|
||||
content_len = http_head_get_fields_value(http_head, "Content-Length");
|
||||
if (content_len) {
|
||||
http_head->expect_data_len = atol(content_len);
|
||||
} else {
|
||||
http_head->expect_data_len = 0;
|
||||
}
|
||||
|
||||
if (http_head->expect_data_len < 0) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process_data_len += i;
|
||||
if (http_head->head_ok == 0) {
|
||||
// Read data again */
|
||||
http_head->buff_len += process_data_len;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (http_head->head_ok == 1) {
|
||||
int get_data_len = (http_head->expect_data_len > data_len) ? data_len : http_head->expect_data_len;
|
||||
if (http_head->data == NULL) {
|
||||
http_head->data = buff_end;
|
||||
}
|
||||
|
||||
memcpy(buff_end, data, get_data_len);
|
||||
process_data_len += get_data_len;
|
||||
http_head->data_len += get_data_len;
|
||||
}
|
||||
|
||||
http_head->buff_len += process_data_len;
|
||||
if (http_head->data_len < http_head->expect_data_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return process_data_len;
|
||||
}
|
||||
|
||||
void http_head_destroy(struct http_head *http_head)
|
||||
{
|
||||
struct http_head_fields *fields, *tmp;
|
||||
|
||||
list_for_each_entry_safe(fields, tmp, &http_head->field_head.list, list)
|
||||
{
|
||||
list_del(&fields->list);
|
||||
free(fields);
|
||||
}
|
||||
|
||||
if (http_head->buff) {
|
||||
free(http_head->buff);
|
||||
}
|
||||
|
||||
free(http_head);
|
||||
}
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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 "http_parse.h"
|
||||
#include "hash.h"
|
||||
#include "hashtable.h"
|
||||
#include "jhash.h"
|
||||
#include "list.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct http_head_fields {
|
||||
struct hlist_node node;
|
||||
struct list_head list;
|
||||
|
||||
char *name;
|
||||
char *value;
|
||||
};
|
||||
|
||||
struct http_head {
|
||||
HTTP_HEAD_TYPE head_type;
|
||||
HTTP_METHOD method;
|
||||
char *url;
|
||||
char *version;
|
||||
int code;
|
||||
char *code_msg;
|
||||
int buff_size;
|
||||
int buff_len;
|
||||
char *buff;
|
||||
int head_ok;
|
||||
int head_len;
|
||||
char *data;
|
||||
int data_len;
|
||||
int expect_data_len;
|
||||
struct http_head_fields field_head;
|
||||
DECLARE_HASHTABLE(field_map, 4);
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns:
|
||||
* >=0 - success http data len
|
||||
* -1 - Incomplete request
|
||||
* -2 - parse failed
|
||||
*/
|
||||
struct http_head *http_head_init(int buffsize)
|
||||
{
|
||||
struct http_head *http_head = NULL;
|
||||
char *buffer = NULL;
|
||||
|
||||
http_head = malloc(sizeof(*http_head));
|
||||
if (http_head == NULL) {
|
||||
goto errout;
|
||||
}
|
||||
memset(http_head, 0, sizeof(*http_head));
|
||||
INIT_LIST_HEAD(&http_head->field_head.list);
|
||||
hash_init(http_head->field_map);
|
||||
|
||||
buffer = malloc(buffsize);
|
||||
if (buffer == NULL) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
http_head->buff = buffer;
|
||||
http_head->buff_size = buffsize;
|
||||
|
||||
return http_head;
|
||||
|
||||
errout:
|
||||
if (buffer) {
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
if (http_head) {
|
||||
free(http_head);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct http_head_fields *http_head_first_fields(struct http_head *http_head)
|
||||
{
|
||||
struct http_head_fields *first = NULL;
|
||||
first = list_first_entry(&http_head->field_head.list, struct http_head_fields, list);
|
||||
|
||||
if (first->name == NULL && first->value == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
const char *http_head_get_fields_value(struct http_head *http_head, const char *name)
|
||||
{
|
||||
unsigned long key;
|
||||
struct http_head_fields *filed;
|
||||
|
||||
key = hash_string(name);
|
||||
hash_for_each_possible(http_head->field_map, filed, node, key)
|
||||
{
|
||||
if (strncmp(filed->name, name, 128) == 0) {
|
||||
return filed->value;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct http_head_fields *http_head_next_fields(struct http_head_fields *fields)
|
||||
{
|
||||
struct http_head_fields *next = NULL;
|
||||
next = list_next_entry(fields, list);
|
||||
|
||||
if (next->name == NULL && next->value == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
int http_head_lookup_fields(struct http_head_fields *fields, const char **name, const char **value)
|
||||
{
|
||||
if (fields == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
*name = fields->name;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
*value = fields->value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
HTTP_METHOD http_head_get_method(struct http_head *http_head)
|
||||
{
|
||||
return http_head->method;
|
||||
}
|
||||
|
||||
const char *http_head_get_url(struct http_head *http_head)
|
||||
{
|
||||
return http_head->url;
|
||||
}
|
||||
|
||||
const char *http_head_get_httpversion(struct http_head *http_head)
|
||||
{
|
||||
return http_head->version;
|
||||
}
|
||||
|
||||
int http_head_get_httpcode(struct http_head *http_head)
|
||||
{
|
||||
return http_head->code;
|
||||
}
|
||||
|
||||
char *http_head_get_httpcode_msg(struct http_head *http_head)
|
||||
{
|
||||
return http_head->code_msg;
|
||||
}
|
||||
|
||||
HTTP_HEAD_TYPE http_head_get_head_type(struct http_head *http_head)
|
||||
{
|
||||
return http_head->head_type;
|
||||
}
|
||||
|
||||
char *http_head_get_data(struct http_head *http_head)
|
||||
{
|
||||
return http_head->data;
|
||||
}
|
||||
|
||||
int http_head_get_data_len(struct http_head *http_head)
|
||||
{
|
||||
return http_head->data_len;
|
||||
}
|
||||
|
||||
static int _http_head_add_fields(struct http_head *http_head, char *name, char *value)
|
||||
{
|
||||
unsigned long key = 0;
|
||||
struct http_head_fields *fields = NULL;
|
||||
fields = malloc(sizeof(*fields));
|
||||
if (fields == NULL) {
|
||||
return -1;
|
||||
}
|
||||
memset(fields, 0, sizeof(*fields));
|
||||
|
||||
fields->name = name;
|
||||
fields->value = value;
|
||||
|
||||
list_add_tail(&fields->list, &http_head->field_head.list);
|
||||
key = hash_string(name);
|
||||
hash_add(http_head->field_map, &fields->node, key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _http_head_parse_response(struct http_head *http_head, char *key, char *value)
|
||||
{
|
||||
char *field_start = NULL;
|
||||
char *tmp_ptr = NULL;
|
||||
char *result = NULL;
|
||||
char *ret_code = NULL;
|
||||
|
||||
if (strstr(key, "HTTP/") == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (tmp_ptr = value; *tmp_ptr != 0; tmp_ptr++) {
|
||||
if (field_start == NULL) {
|
||||
field_start = tmp_ptr;
|
||||
}
|
||||
|
||||
if (*tmp_ptr == ' ') {
|
||||
*tmp_ptr = '\0';
|
||||
if (ret_code == NULL) {
|
||||
ret_code = field_start;
|
||||
} else if (result == NULL) {
|
||||
result = field_start;
|
||||
break;
|
||||
}
|
||||
|
||||
field_start = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (field_start && result == NULL) {
|
||||
result = field_start;
|
||||
}
|
||||
|
||||
if (ret_code == NULL || result == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
http_head->code = atol(ret_code);
|
||||
http_head->code_msg = result;
|
||||
http_head->version = key;
|
||||
http_head->head_type = HTTP_HEAD_RESPONSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _http_head_parse_request(struct http_head *http_head, char *key, char *value)
|
||||
{
|
||||
int method = HTTP_METHOD_INVALID;
|
||||
char *url = NULL;
|
||||
char *version = NULL;
|
||||
char *tmp_ptr = value;
|
||||
char *field_start = NULL;
|
||||
|
||||
if (strncmp(key, "GET", sizeof("GET")) == 0) {
|
||||
method = HTTP_METHOD_GET;
|
||||
} else if (strncmp(key, "POST", sizeof("POST")) == 0) {
|
||||
method = HTTP_METHOD_POST;
|
||||
} else if (strncmp(key, "PUT", sizeof("PUT")) == 0) {
|
||||
method = HTTP_METHOD_PUT;
|
||||
} else if (strncmp(key, "DELETE", sizeof("DELETE")) == 0) {
|
||||
method = HTTP_METHOD_DELETE;
|
||||
} else if (strncmp(key, "TRACE", sizeof("TRACE")) == 0) {
|
||||
method = HTTP_METHOD_TRACE;
|
||||
} else if (strncmp(key, "CONNECT", sizeof("CONNECT")) == 0) {
|
||||
method = HTTP_METHOD_CONNECT;
|
||||
} else {
|
||||
return _http_head_parse_response(http_head, key, value);
|
||||
}
|
||||
|
||||
for (tmp_ptr = value; *tmp_ptr != 0; tmp_ptr++) {
|
||||
if (field_start == NULL) {
|
||||
field_start = tmp_ptr;
|
||||
}
|
||||
if (*tmp_ptr == ' ') {
|
||||
*tmp_ptr = '\0';
|
||||
if (url == NULL) {
|
||||
url = field_start;
|
||||
}
|
||||
|
||||
field_start = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (field_start && version == NULL) {
|
||||
version = field_start;
|
||||
}
|
||||
|
||||
http_head->method = method;
|
||||
http_head->url = url;
|
||||
http_head->version = version;
|
||||
http_head->head_type = HTTP_HEAD_REQUEST;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _http_head_parse(struct http_head *http_head)
|
||||
{
|
||||
int i = 0;
|
||||
char *key = NULL;
|
||||
char *value = NULL;
|
||||
char *data;
|
||||
int has_first_line = 0;
|
||||
|
||||
int inkey = 1;
|
||||
int invalue = 0;
|
||||
|
||||
data = http_head->buff;
|
||||
for (i = 0; i < http_head->head_len; i++, data++) {
|
||||
if (inkey) {
|
||||
if (key == NULL && *data != ' ' && *data != '\r' && *data != '\n') {
|
||||
key = data;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*data == ':' || *data == ' ') {
|
||||
*data = '\0';
|
||||
inkey = 0;
|
||||
invalue = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalue) {
|
||||
if (value == NULL && *data != ' ') {
|
||||
value = data;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*data == '\r' || *data == '\n') {
|
||||
*data = '\0';
|
||||
inkey = 1;
|
||||
invalue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (key && value && invalue == 0) {
|
||||
if (has_first_line == 0) {
|
||||
if (_http_head_parse_request(http_head, key, value) != 0) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
has_first_line = 1;
|
||||
} else {
|
||||
if (_http_head_add_fields(http_head, key, value) != 0) {
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
key = NULL;
|
||||
value = NULL;
|
||||
inkey = 1;
|
||||
invalue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_head_parse(struct http_head *http_head, const char *data, int data_len)
|
||||
{
|
||||
int i = 0;
|
||||
char *buff_end = NULL;
|
||||
int left_size = 0;
|
||||
int process_data_len = 0;
|
||||
|
||||
left_size = http_head->buff_size - http_head->buff_len;
|
||||
|
||||
if (left_size < data_len) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
buff_end = http_head->buff + http_head->buff_len;
|
||||
if (http_head->head_ok == 0) {
|
||||
for (i = 0; i < data_len; i++, data++) {
|
||||
*(buff_end + i) = *data;
|
||||
if (*data == '\n') {
|
||||
if (http_head->buff_len + i < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*(buff_end + i - 2) == '\n') {
|
||||
http_head->head_ok = 1;
|
||||
http_head->head_len = http_head->buff_len + i - 2;
|
||||
i++;
|
||||
buff_end += i;
|
||||
data_len -= i;
|
||||
data++;
|
||||
if (_http_head_parse(http_head) != 0) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
const char *content_len = NULL;
|
||||
content_len = http_head_get_fields_value(http_head, "Content-Length");
|
||||
if (content_len) {
|
||||
http_head->expect_data_len = atol(content_len);
|
||||
} else {
|
||||
http_head->expect_data_len = 0;
|
||||
}
|
||||
|
||||
if (http_head->expect_data_len < 0) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process_data_len += i;
|
||||
if (http_head->head_ok == 0) {
|
||||
// Read data again */
|
||||
http_head->buff_len += process_data_len;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (http_head->head_ok == 1) {
|
||||
int get_data_len = (http_head->expect_data_len > data_len) ? data_len : http_head->expect_data_len;
|
||||
if (http_head->data == NULL) {
|
||||
http_head->data = buff_end;
|
||||
}
|
||||
|
||||
memcpy(buff_end, data, get_data_len);
|
||||
process_data_len += get_data_len;
|
||||
http_head->data_len += get_data_len;
|
||||
}
|
||||
|
||||
http_head->buff_len += process_data_len;
|
||||
if (http_head->data_len < http_head->expect_data_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return process_data_len;
|
||||
}
|
||||
|
||||
void http_head_destroy(struct http_head *http_head)
|
||||
{
|
||||
struct http_head_fields *fields, *tmp;
|
||||
|
||||
list_for_each_entry_safe(fields, tmp, &http_head->field_head.list, list)
|
||||
{
|
||||
list_del(&fields->list);
|
||||
free(fields);
|
||||
}
|
||||
|
||||
if (http_head->buff) {
|
||||
free(http_head->buff);
|
||||
}
|
||||
|
||||
free(http_head);
|
||||
}
|
||||
|
||||
174
src/http_parse.h
174
src/http_parse.h
@@ -1,87 +1,87 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#ifndef HTTP_PARSER_H
|
||||
#define HTTP_PARSER_H
|
||||
|
||||
#ifdef __cpluscplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct http_head;
|
||||
struct http_head_fields;
|
||||
|
||||
typedef enum HTTP_METHOD {
|
||||
HTTP_METHOD_INVALID = 0,
|
||||
HTTP_METHOD_GET,
|
||||
HTTP_METHOD_HEAD,
|
||||
HTTP_METHOD_POST,
|
||||
HTTP_METHOD_PUT,
|
||||
HTTP_METHOD_DELETE,
|
||||
HTTP_METHOD_TRACE,
|
||||
HTTP_METHOD_CONNECT,
|
||||
} HTTP_METHOD;
|
||||
|
||||
typedef enum HTTP_HEAD_TYPE {
|
||||
HTTP_HEAD_INVALID = 0,
|
||||
HTTP_HEAD_REQUEST = 1,
|
||||
HTTP_HEAD_RESPONSE = 2,
|
||||
} HTTP_HEAD_TYPE;
|
||||
|
||||
struct http_head *http_head_init(int buffsize);
|
||||
|
||||
HTTP_HEAD_TYPE http_head_get_head_type(struct http_head *http_head);
|
||||
|
||||
HTTP_METHOD http_head_get_method(struct http_head *http_head);
|
||||
|
||||
const char *http_head_get_url(struct http_head *http_head);
|
||||
|
||||
const char *http_head_get_httpversion(struct http_head *http_head);
|
||||
|
||||
int http_head_get_httpcode(struct http_head *http_head);
|
||||
|
||||
char *http_head_get_httpcode_msg(struct http_head *http_head);
|
||||
|
||||
char *http_head_get_data(struct http_head *http_head);
|
||||
|
||||
int http_head_get_data_len(struct http_head *http_head);
|
||||
|
||||
struct http_head_fields *http_head_first_fields(struct http_head *http_head);
|
||||
|
||||
struct http_head_fields *http_head_next_fields(struct http_head_fields *fields);
|
||||
|
||||
const char *http_head_get_fields_value(struct http_head *http_head, const char *name);
|
||||
|
||||
int http_head_lookup_fields(struct http_head_fields *fields, const char **name, const char **value);
|
||||
|
||||
/*
|
||||
* Returns:
|
||||
* >=0 - success http data len
|
||||
* -1 - Incomplete request
|
||||
* -2 - parse failed
|
||||
* -3 - buffer is small
|
||||
*/
|
||||
int http_head_parse(struct http_head *http_head, const char *data, int data_len);
|
||||
|
||||
void http_head_destroy(struct http_head *http_head);
|
||||
|
||||
#ifdef __cpluscplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // !HTTP_PARSER_H
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#ifndef HTTP_PARSER_H
|
||||
#define HTTP_PARSER_H
|
||||
|
||||
#ifdef __cpluscplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct http_head;
|
||||
struct http_head_fields;
|
||||
|
||||
typedef enum HTTP_METHOD {
|
||||
HTTP_METHOD_INVALID = 0,
|
||||
HTTP_METHOD_GET,
|
||||
HTTP_METHOD_HEAD,
|
||||
HTTP_METHOD_POST,
|
||||
HTTP_METHOD_PUT,
|
||||
HTTP_METHOD_DELETE,
|
||||
HTTP_METHOD_TRACE,
|
||||
HTTP_METHOD_CONNECT,
|
||||
} HTTP_METHOD;
|
||||
|
||||
typedef enum HTTP_HEAD_TYPE {
|
||||
HTTP_HEAD_INVALID = 0,
|
||||
HTTP_HEAD_REQUEST = 1,
|
||||
HTTP_HEAD_RESPONSE = 2,
|
||||
} HTTP_HEAD_TYPE;
|
||||
|
||||
struct http_head *http_head_init(int buffsize);
|
||||
|
||||
HTTP_HEAD_TYPE http_head_get_head_type(struct http_head *http_head);
|
||||
|
||||
HTTP_METHOD http_head_get_method(struct http_head *http_head);
|
||||
|
||||
const char *http_head_get_url(struct http_head *http_head);
|
||||
|
||||
const char *http_head_get_httpversion(struct http_head *http_head);
|
||||
|
||||
int http_head_get_httpcode(struct http_head *http_head);
|
||||
|
||||
char *http_head_get_httpcode_msg(struct http_head *http_head);
|
||||
|
||||
char *http_head_get_data(struct http_head *http_head);
|
||||
|
||||
int http_head_get_data_len(struct http_head *http_head);
|
||||
|
||||
struct http_head_fields *http_head_first_fields(struct http_head *http_head);
|
||||
|
||||
struct http_head_fields *http_head_next_fields(struct http_head_fields *fields);
|
||||
|
||||
const char *http_head_get_fields_value(struct http_head *http_head, const char *name);
|
||||
|
||||
int http_head_lookup_fields(struct http_head_fields *fields, const char **name, const char **value);
|
||||
|
||||
/*
|
||||
* Returns:
|
||||
* >=0 - success http data len
|
||||
* -1 - Incomplete request
|
||||
* -2 - parse failed
|
||||
* -3 - buffer is small
|
||||
*/
|
||||
int http_head_parse(struct http_head *http_head, const char *data, int data_len);
|
||||
|
||||
void http_head_destroy(struct http_head *http_head);
|
||||
|
||||
#ifdef __cpluscplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // !HTTP_PARSER_H
|
||||
|
||||
@@ -1,251 +1,251 @@
|
||||
/*
|
||||
Copyright (c) 2012, Armon Dadgar
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the organization nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL ARMON DADGAR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#ifndef ART_H
|
||||
#define ART_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define NODE4 1
|
||||
#define NODE16 2
|
||||
#define NODE48 3
|
||||
#define NODE256 4
|
||||
|
||||
#define MAX_PREFIX_LEN 10
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
# if __STDC_VERSION__ >= 199901L && 402 == (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||
/*
|
||||
* GCC 4.2.2's C99 inline keyword support is pretty broken; avoid. Introduced in
|
||||
* GCC 4.2.something, fixed in 4.3.0. So checking for specific major.minor of
|
||||
* 4.2 is fine.
|
||||
*/
|
||||
# define BROKEN_GCC_C99_INLINE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
typedef int(*art_callback)(void *data, const unsigned char *key, uint32_t key_len, void *value);
|
||||
|
||||
/**
|
||||
* This struct is included as part
|
||||
* of all the various node sizes
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t type;
|
||||
uint8_t num_children;
|
||||
uint32_t partial_len;
|
||||
unsigned char partial[MAX_PREFIX_LEN];
|
||||
} art_node;
|
||||
|
||||
/**
|
||||
* Small node with only 4 children
|
||||
*/
|
||||
typedef struct {
|
||||
art_node n;
|
||||
unsigned char keys[4];
|
||||
art_node *children[4];
|
||||
} art_node4;
|
||||
|
||||
/**
|
||||
* Node with 16 children
|
||||
*/
|
||||
typedef struct {
|
||||
art_node n;
|
||||
unsigned char keys[16];
|
||||
art_node *children[16];
|
||||
} art_node16;
|
||||
|
||||
/**
|
||||
* Node with 48 children, but
|
||||
* a full 256 byte field.
|
||||
*/
|
||||
typedef struct {
|
||||
art_node n;
|
||||
unsigned char keys[256];
|
||||
art_node *children[48];
|
||||
} art_node48;
|
||||
|
||||
/**
|
||||
* Full node with 256 children
|
||||
*/
|
||||
typedef struct {
|
||||
art_node n;
|
||||
art_node *children[256];
|
||||
} art_node256;
|
||||
|
||||
/**
|
||||
* Represents a leaf. These are
|
||||
* of arbitrary size, as they include the key.
|
||||
*/
|
||||
typedef struct {
|
||||
void *value;
|
||||
uint32_t key_len;
|
||||
unsigned char key[0];
|
||||
} art_leaf;
|
||||
|
||||
/**
|
||||
* Main struct, points to root.
|
||||
*/
|
||||
typedef struct {
|
||||
art_node *root;
|
||||
uint64_t size;
|
||||
} art_tree;
|
||||
|
||||
/**
|
||||
* Initializes an ART tree
|
||||
* @return 0 on success.
|
||||
*/
|
||||
int art_tree_init(art_tree *t);
|
||||
|
||||
/**
|
||||
* DEPRECATED
|
||||
* Initializes an ART tree
|
||||
* @return 0 on success.
|
||||
*/
|
||||
#define init_art_tree(...) art_tree_init(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Destroys an ART tree
|
||||
* @return 0 on success.
|
||||
*/
|
||||
int art_tree_destroy(art_tree *t);
|
||||
|
||||
/**
|
||||
* DEPRECATED
|
||||
* Initializes an ART tree
|
||||
* @return 0 on success.
|
||||
*/
|
||||
#define destroy_art_tree(...) art_tree_destroy(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Returns the size of the ART tree.
|
||||
*/
|
||||
#ifdef BROKEN_GCC_C99_INLINE
|
||||
# define art_size(t) ((t)->size)
|
||||
#else
|
||||
static inline uint64_t art_size(art_tree *t) {
|
||||
return t->size;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Inserts a new value into the ART tree
|
||||
* @arg t The tree
|
||||
* @arg key The key
|
||||
* @arg key_len The length of the key
|
||||
* @arg value Opaque value.
|
||||
* @return NULL if the item was newly inserted, otherwise
|
||||
* the old value pointer is returned.
|
||||
*/
|
||||
void* art_insert(art_tree *t, const unsigned char *key, int key_len, void *value);
|
||||
|
||||
/**
|
||||
* Deletes a value from the ART tree
|
||||
* @arg t The tree
|
||||
* @arg key The key
|
||||
* @arg key_len The length of the key
|
||||
* @return NULL if the item was not found, otherwise
|
||||
* the value pointer is returned.
|
||||
*/
|
||||
void* art_delete(art_tree *t, const unsigned char *key, int key_len);
|
||||
|
||||
/**
|
||||
* Searches for a value in the ART tree
|
||||
* @arg t The tree
|
||||
* @arg key The key
|
||||
* @arg key_len The length of the key
|
||||
* @return NULL if the item was not found, otherwise
|
||||
* the value pointer is returned.
|
||||
*/
|
||||
void* art_search(const art_tree *t, const unsigned char *key, int key_len);
|
||||
|
||||
/**
|
||||
* Searches substring for a value in the ART tree
|
||||
* @arg t The tree
|
||||
* @arg str The key
|
||||
* @arg str_len The length of the key
|
||||
* @return NULL if the item was not found, otherwise
|
||||
* the value pointer is returned.
|
||||
*/
|
||||
void *art_substring(const art_tree *t, const unsigned char *str, int str_len, unsigned char *key, int *key_len);
|
||||
|
||||
/**
|
||||
* Wakk substring for a value in the ART tree
|
||||
* @arg t The tree
|
||||
* @arg str The key
|
||||
* @arg str_len The length of the key
|
||||
* @return NULL if the item was not found, otherwise
|
||||
* the value pointer is returned.
|
||||
*/
|
||||
typedef int (*walk_func)(unsigned char *key, uint32_t key_len, void *value, void *arg);
|
||||
void art_substring_walk(const art_tree *t, const unsigned char *str, int str_len, walk_func func, void *arg);
|
||||
|
||||
/**
|
||||
* Returns the minimum valued leaf
|
||||
* @return The minimum leaf or NULL
|
||||
*/
|
||||
art_leaf* art_minimum(art_tree *t);
|
||||
|
||||
/**
|
||||
* Returns the maximum valued leaf
|
||||
* @return The maximum leaf or NULL
|
||||
*/
|
||||
art_leaf* art_maximum(art_tree *t);
|
||||
|
||||
/**
|
||||
* Iterates through the entries pairs in the map,
|
||||
* invoking a callback for each. The call back gets a
|
||||
* key, value for each and returns an integer stop value.
|
||||
* If the callback returns non-zero, then the iteration stops.
|
||||
* @arg t The tree to iterate over
|
||||
* @arg cb The callback function to invoke
|
||||
* @arg data Opaque handle passed to the callback
|
||||
* @return 0 on success, or the return of the callback.
|
||||
*/
|
||||
int art_iter(art_tree *t, art_callback cb, void *data);
|
||||
|
||||
/**
|
||||
* Iterates through the entries pairs in the map,
|
||||
* invoking a callback for each that matches a given prefix.
|
||||
* The call back gets a key, value for each and returns an integer stop value.
|
||||
* If the callback returns non-zero, then the iteration stops.
|
||||
* @arg t The tree to iterate over
|
||||
* @arg prefix The prefix of keys to read
|
||||
* @arg prefix_len The length of the prefix
|
||||
* @arg cb The callback function to invoke
|
||||
* @arg data Opaque handle passed to the callback
|
||||
* @return 0 on success, or the return of the callback.
|
||||
*/
|
||||
int art_iter_prefix(art_tree *t, const unsigned char *prefix, int prefix_len, art_callback cb, void *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/*
|
||||
Copyright (c) 2012, Armon Dadgar
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the organization nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL ARMON DADGAR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#ifndef ART_H
|
||||
#define ART_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define NODE4 1
|
||||
#define NODE16 2
|
||||
#define NODE48 3
|
||||
#define NODE256 4
|
||||
|
||||
#define MAX_PREFIX_LEN 10
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
# if __STDC_VERSION__ >= 199901L && 402 == (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||
/*
|
||||
* GCC 4.2.2's C99 inline keyword support is pretty broken; avoid. Introduced in
|
||||
* GCC 4.2.something, fixed in 4.3.0. So checking for specific major.minor of
|
||||
* 4.2 is fine.
|
||||
*/
|
||||
# define BROKEN_GCC_C99_INLINE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
typedef int(*art_callback)(void *data, const unsigned char *key, uint32_t key_len, void *value);
|
||||
|
||||
/**
|
||||
* This struct is included as part
|
||||
* of all the various node sizes
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t type;
|
||||
uint8_t num_children;
|
||||
uint32_t partial_len;
|
||||
unsigned char partial[MAX_PREFIX_LEN];
|
||||
} art_node;
|
||||
|
||||
/**
|
||||
* Small node with only 4 children
|
||||
*/
|
||||
typedef struct {
|
||||
art_node n;
|
||||
unsigned char keys[4];
|
||||
art_node *children[4];
|
||||
} art_node4;
|
||||
|
||||
/**
|
||||
* Node with 16 children
|
||||
*/
|
||||
typedef struct {
|
||||
art_node n;
|
||||
unsigned char keys[16];
|
||||
art_node *children[16];
|
||||
} art_node16;
|
||||
|
||||
/**
|
||||
* Node with 48 children, but
|
||||
* a full 256 byte field.
|
||||
*/
|
||||
typedef struct {
|
||||
art_node n;
|
||||
unsigned char keys[256];
|
||||
art_node *children[48];
|
||||
} art_node48;
|
||||
|
||||
/**
|
||||
* Full node with 256 children
|
||||
*/
|
||||
typedef struct {
|
||||
art_node n;
|
||||
art_node *children[256];
|
||||
} art_node256;
|
||||
|
||||
/**
|
||||
* Represents a leaf. These are
|
||||
* of arbitrary size, as they include the key.
|
||||
*/
|
||||
typedef struct {
|
||||
void *value;
|
||||
uint32_t key_len;
|
||||
unsigned char key[0];
|
||||
} art_leaf;
|
||||
|
||||
/**
|
||||
* Main struct, points to root.
|
||||
*/
|
||||
typedef struct {
|
||||
art_node *root;
|
||||
uint64_t size;
|
||||
} art_tree;
|
||||
|
||||
/**
|
||||
* Initializes an ART tree
|
||||
* @return 0 on success.
|
||||
*/
|
||||
int art_tree_init(art_tree *t);
|
||||
|
||||
/**
|
||||
* DEPRECATED
|
||||
* Initializes an ART tree
|
||||
* @return 0 on success.
|
||||
*/
|
||||
#define init_art_tree(...) art_tree_init(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Destroys an ART tree
|
||||
* @return 0 on success.
|
||||
*/
|
||||
int art_tree_destroy(art_tree *t);
|
||||
|
||||
/**
|
||||
* DEPRECATED
|
||||
* Initializes an ART tree
|
||||
* @return 0 on success.
|
||||
*/
|
||||
#define destroy_art_tree(...) art_tree_destroy(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Returns the size of the ART tree.
|
||||
*/
|
||||
#ifdef BROKEN_GCC_C99_INLINE
|
||||
# define art_size(t) ((t)->size)
|
||||
#else
|
||||
static inline uint64_t art_size(art_tree *t) {
|
||||
return t->size;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Inserts a new value into the ART tree
|
||||
* @arg t The tree
|
||||
* @arg key The key
|
||||
* @arg key_len The length of the key
|
||||
* @arg value Opaque value.
|
||||
* @return NULL if the item was newly inserted, otherwise
|
||||
* the old value pointer is returned.
|
||||
*/
|
||||
void* art_insert(art_tree *t, const unsigned char *key, int key_len, void *value);
|
||||
|
||||
/**
|
||||
* Deletes a value from the ART tree
|
||||
* @arg t The tree
|
||||
* @arg key The key
|
||||
* @arg key_len The length of the key
|
||||
* @return NULL if the item was not found, otherwise
|
||||
* the value pointer is returned.
|
||||
*/
|
||||
void* art_delete(art_tree *t, const unsigned char *key, int key_len);
|
||||
|
||||
/**
|
||||
* Searches for a value in the ART tree
|
||||
* @arg t The tree
|
||||
* @arg key The key
|
||||
* @arg key_len The length of the key
|
||||
* @return NULL if the item was not found, otherwise
|
||||
* the value pointer is returned.
|
||||
*/
|
||||
void* art_search(const art_tree *t, const unsigned char *key, int key_len);
|
||||
|
||||
/**
|
||||
* Searches substring for a value in the ART tree
|
||||
* @arg t The tree
|
||||
* @arg str The key
|
||||
* @arg str_len The length of the key
|
||||
* @return NULL if the item was not found, otherwise
|
||||
* the value pointer is returned.
|
||||
*/
|
||||
void *art_substring(const art_tree *t, const unsigned char *str, int str_len, unsigned char *key, int *key_len);
|
||||
|
||||
/**
|
||||
* Wakk substring for a value in the ART tree
|
||||
* @arg t The tree
|
||||
* @arg str The key
|
||||
* @arg str_len The length of the key
|
||||
* @return NULL if the item was not found, otherwise
|
||||
* the value pointer is returned.
|
||||
*/
|
||||
typedef int (*walk_func)(unsigned char *key, uint32_t key_len, void *value, void *arg);
|
||||
void art_substring_walk(const art_tree *t, const unsigned char *str, int str_len, walk_func func, void *arg);
|
||||
|
||||
/**
|
||||
* Returns the minimum valued leaf
|
||||
* @return The minimum leaf or NULL
|
||||
*/
|
||||
art_leaf* art_minimum(art_tree *t);
|
||||
|
||||
/**
|
||||
* Returns the maximum valued leaf
|
||||
* @return The maximum leaf or NULL
|
||||
*/
|
||||
art_leaf* art_maximum(art_tree *t);
|
||||
|
||||
/**
|
||||
* Iterates through the entries pairs in the map,
|
||||
* invoking a callback for each. The call back gets a
|
||||
* key, value for each and returns an integer stop value.
|
||||
* If the callback returns non-zero, then the iteration stops.
|
||||
* @arg t The tree to iterate over
|
||||
* @arg cb The callback function to invoke
|
||||
* @arg data Opaque handle passed to the callback
|
||||
* @return 0 on success, or the return of the callback.
|
||||
*/
|
||||
int art_iter(art_tree *t, art_callback cb, void *data);
|
||||
|
||||
/**
|
||||
* Iterates through the entries pairs in the map,
|
||||
* invoking a callback for each that matches a given prefix.
|
||||
* The call back gets a key, value for each and returns an integer stop value.
|
||||
* If the callback returns non-zero, then the iteration stops.
|
||||
* @arg t The tree to iterate over
|
||||
* @arg prefix The prefix of keys to read
|
||||
* @arg prefix_len The length of the prefix
|
||||
* @arg cb The callback function to invoke
|
||||
* @arg data Opaque handle passed to the callback
|
||||
* @return 0 on success, or the return of the callback.
|
||||
*/
|
||||
int art_iter_prefix(art_tree *t, const unsigned char *prefix, int prefix_len, art_callback cb, void *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,142 +1,142 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _GENERIC_CONF_H
|
||||
#define _GENERIC_CONF_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#define MAX_LINE_LEN 1024
|
||||
#define MAX_KEY_LEN 64
|
||||
#define CONF_INT_MAX (~(1 << 31))
|
||||
#define CONF_INT_MIN (1 << 31)
|
||||
|
||||
#define CONF_RET_OK 0
|
||||
#define CONF_RET_ERR -1
|
||||
#define CONF_RET_WARN -2
|
||||
#define CONF_RET_NOENT -3
|
||||
|
||||
struct config_item {
|
||||
const char *item;
|
||||
int (*item_func)(const char *item, void *data, int argc, char *argv[]);
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct config_item_custom {
|
||||
void *custom_data;
|
||||
int (*custom_func)(void *data, int argc, char *argv[]);
|
||||
};
|
||||
|
||||
struct config_item_int {
|
||||
int *data;
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
struct config_item_string {
|
||||
char *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct config_item_yesno {
|
||||
int *data;
|
||||
};
|
||||
|
||||
struct config_item_size {
|
||||
size_t *data;
|
||||
size_t min;
|
||||
size_t max;
|
||||
};
|
||||
|
||||
#define CONF_INT(key, value, min_value, max_value) \
|
||||
{ \
|
||||
key, conf_int, &(struct config_item_int) \
|
||||
{ \
|
||||
.data = value, .min = min_value, .max = max_value \
|
||||
} \
|
||||
}
|
||||
#define CONF_STRING(key, value, len_value) \
|
||||
{ \
|
||||
key, conf_string, &(struct config_item_string) \
|
||||
{ \
|
||||
.data = value, .size = len_value \
|
||||
} \
|
||||
}
|
||||
#define CONF_YESNO(key, value) \
|
||||
{ \
|
||||
key, conf_yesno, &(struct config_item_yesno) \
|
||||
{ \
|
||||
.data = value \
|
||||
} \
|
||||
}
|
||||
#define CONF_SIZE(key, value, min_value, max_value) \
|
||||
{ \
|
||||
key, conf_size, &(struct config_item_size) \
|
||||
{ \
|
||||
.data = value, .min = min_value, .max = max_value \
|
||||
} \
|
||||
}
|
||||
/*
|
||||
* func: int (*func)(void *data, int argc, char *argv[]);
|
||||
*/
|
||||
#define CONF_CUSTOM(key, func, data) \
|
||||
{ \
|
||||
key, conf_custom, &(struct config_item_custom) \
|
||||
{ \
|
||||
.custom_data = data, .custom_func = func \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CONF_END() \
|
||||
{ \
|
||||
NULL, NULL, NULL \
|
||||
}
|
||||
|
||||
extern int conf_custom(const char *item, void *data, int argc, char *argv[]);
|
||||
|
||||
extern int conf_int(const char *item, void *data, int argc, char *argv[]);
|
||||
|
||||
extern int conf_string(const char *item, void *data, int argc, char *argv[]);
|
||||
|
||||
extern int conf_yesno(const char *item, void *data, int argc, char *argv[]);
|
||||
|
||||
extern int conf_size(const char *item, void *data, int argc, char *argv[]);
|
||||
|
||||
/*
|
||||
* Example:
|
||||
* int num = 0;
|
||||
*
|
||||
* struct config_item itmes [] = {
|
||||
* CONF_INT("CONF_NUM", &num, -1, 10),
|
||||
* CONF_END();
|
||||
* }
|
||||
*
|
||||
* load_conf(file, items);
|
||||
*
|
||||
*/
|
||||
|
||||
typedef int(conf_error_handler)(const char *file, int lineno, int ret);
|
||||
|
||||
int load_conf(const char *file, struct config_item items[], conf_error_handler handler);
|
||||
|
||||
void load_exit(void);
|
||||
|
||||
const char *conf_get_conf_file(void);
|
||||
|
||||
#endif // !_GENERIC_CONF_H
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _GENERIC_CONF_H
|
||||
#define _GENERIC_CONF_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#define MAX_LINE_LEN 1024
|
||||
#define MAX_KEY_LEN 64
|
||||
#define CONF_INT_MAX (~(1 << 31))
|
||||
#define CONF_INT_MIN (1 << 31)
|
||||
|
||||
#define CONF_RET_OK 0
|
||||
#define CONF_RET_ERR -1
|
||||
#define CONF_RET_WARN -2
|
||||
#define CONF_RET_NOENT -3
|
||||
|
||||
struct config_item {
|
||||
const char *item;
|
||||
int (*item_func)(const char *item, void *data, int argc, char *argv[]);
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct config_item_custom {
|
||||
void *custom_data;
|
||||
int (*custom_func)(void *data, int argc, char *argv[]);
|
||||
};
|
||||
|
||||
struct config_item_int {
|
||||
int *data;
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
struct config_item_string {
|
||||
char *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct config_item_yesno {
|
||||
int *data;
|
||||
};
|
||||
|
||||
struct config_item_size {
|
||||
size_t *data;
|
||||
size_t min;
|
||||
size_t max;
|
||||
};
|
||||
|
||||
#define CONF_INT(key, value, min_value, max_value) \
|
||||
{ \
|
||||
key, conf_int, &(struct config_item_int) \
|
||||
{ \
|
||||
.data = value, .min = min_value, .max = max_value \
|
||||
} \
|
||||
}
|
||||
#define CONF_STRING(key, value, len_value) \
|
||||
{ \
|
||||
key, conf_string, &(struct config_item_string) \
|
||||
{ \
|
||||
.data = value, .size = len_value \
|
||||
} \
|
||||
}
|
||||
#define CONF_YESNO(key, value) \
|
||||
{ \
|
||||
key, conf_yesno, &(struct config_item_yesno) \
|
||||
{ \
|
||||
.data = value \
|
||||
} \
|
||||
}
|
||||
#define CONF_SIZE(key, value, min_value, max_value) \
|
||||
{ \
|
||||
key, conf_size, &(struct config_item_size) \
|
||||
{ \
|
||||
.data = value, .min = min_value, .max = max_value \
|
||||
} \
|
||||
}
|
||||
/*
|
||||
* func: int (*func)(void *data, int argc, char *argv[]);
|
||||
*/
|
||||
#define CONF_CUSTOM(key, func, data) \
|
||||
{ \
|
||||
key, conf_custom, &(struct config_item_custom) \
|
||||
{ \
|
||||
.custom_data = data, .custom_func = func \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CONF_END() \
|
||||
{ \
|
||||
NULL, NULL, NULL \
|
||||
}
|
||||
|
||||
extern int conf_custom(const char *item, void *data, int argc, char *argv[]);
|
||||
|
||||
extern int conf_int(const char *item, void *data, int argc, char *argv[]);
|
||||
|
||||
extern int conf_string(const char *item, void *data, int argc, char *argv[]);
|
||||
|
||||
extern int conf_yesno(const char *item, void *data, int argc, char *argv[]);
|
||||
|
||||
extern int conf_size(const char *item, void *data, int argc, char *argv[]);
|
||||
|
||||
/*
|
||||
* Example:
|
||||
* int num = 0;
|
||||
*
|
||||
* struct config_item itmes [] = {
|
||||
* CONF_INT("CONF_NUM", &num, -1, 10),
|
||||
* CONF_END();
|
||||
* }
|
||||
*
|
||||
* load_conf(file, items);
|
||||
*
|
||||
*/
|
||||
|
||||
typedef int(conf_error_handler)(const char *file, int lineno, int ret);
|
||||
|
||||
int load_conf(const char *file, struct config_item items[], conf_error_handler handler);
|
||||
|
||||
void load_exit(void);
|
||||
|
||||
const char *conf_get_conf_file(void);
|
||||
|
||||
#endif // !_GENERIC_CONF_H
|
||||
|
||||
@@ -1,162 +1,162 @@
|
||||
/*
|
||||
* Copyright (c) 1999-2000
|
||||
*
|
||||
* The Regents of the University of Michigan ("The Regents") and
|
||||
* Merit Network, Inc. All rights reserved. Redistribution and use
|
||||
* in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of
|
||||
* this software must display the following acknowledgement:
|
||||
*
|
||||
* This product includes software developed by the University of
|
||||
* Michigan, Merit Network, Inc., and their contributors.
|
||||
*
|
||||
* 4. Neither the name of the University, Merit Network, nor the
|
||||
* names of their contributors may be used to endorse or
|
||||
* promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TH E REGENTS
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HO WEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*
|
||||
* Portions Copyright (c) 2004,2005 Damien Miller <djm@mindrot.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: radix.h,v 1.9 2007/10/24 06:03:08 djm Exp $ */
|
||||
|
||||
#ifndef _RADIX_H
|
||||
#define _RADIX_H
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <sys/socket.h>
|
||||
# include <netinet/in.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <netdb.h>
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define snprintf _snprintf
|
||||
typedef unsigned __int8 u_int8_t;
|
||||
typedef unsigned __int16 u_int16_t;
|
||||
typedef unsigned __int32 u_int32_t;
|
||||
const char *inet_ntop(int af, const void *src, char *dst, size_t size);
|
||||
size_t strlcpy(char *dst, const char *src, size_t size);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Originally from MRT include/mrt.h
|
||||
* $MRTId: mrt.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $
|
||||
*/
|
||||
typedef struct _prefix_t {
|
||||
u_int family; /* AF_INET | AF_INET6 */
|
||||
u_int bitlen; /* same as mask? */
|
||||
int ref_count; /* reference count */
|
||||
union {
|
||||
struct in_addr sin;
|
||||
struct in6_addr sin6;
|
||||
} add;
|
||||
} prefix_t;
|
||||
|
||||
void Deref_Prefix(prefix_t *prefix);
|
||||
|
||||
/*
|
||||
* Originally from MRT include/radix.h
|
||||
* $MRTId: radix.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $
|
||||
*/
|
||||
typedef struct _radix_node_t {
|
||||
u_int bit; /* flag if this node used */
|
||||
prefix_t *prefix; /* who we are in radix tree */
|
||||
struct _radix_node_t *l, *r; /* left and right children */
|
||||
struct _radix_node_t *parent; /* may be used */
|
||||
void *data; /* pointer to data */
|
||||
} radix_node_t;
|
||||
|
||||
typedef struct _radix_tree_t {
|
||||
radix_node_t *head;
|
||||
u_int maxbits; /* for IP, 32 bit addresses */
|
||||
int num_active_node; /* for debug purpose */
|
||||
} radix_tree_t;
|
||||
|
||||
/* Type of callback function */
|
||||
typedef void (*rdx_cb_t)(radix_node_t *, void *);
|
||||
|
||||
radix_tree_t *New_Radix(void);
|
||||
void Destroy_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx);
|
||||
radix_node_t *radix_lookup(radix_tree_t *radix, prefix_t *prefix);
|
||||
void radix_remove(radix_tree_t *radix, radix_node_t *node);
|
||||
radix_node_t *radix_search_exact(radix_tree_t *radix, prefix_t *prefix);
|
||||
radix_node_t *radix_search_best(radix_tree_t *radix, prefix_t *prefix);
|
||||
void radix_process(radix_tree_t *radix, rdx_cb_t func, void *cbctx);
|
||||
|
||||
#define RADIX_MAXBITS 128
|
||||
|
||||
#define RADIX_WALK(Xhead, Xnode) \
|
||||
do { \
|
||||
radix_node_t *Xstack[RADIX_MAXBITS+1]; \
|
||||
radix_node_t **Xsp = Xstack; \
|
||||
radix_node_t *Xrn = (Xhead); \
|
||||
while ((Xnode = Xrn)) { \
|
||||
if (Xnode->prefix)
|
||||
|
||||
#define RADIX_WALK_END \
|
||||
if (Xrn->l) { \
|
||||
if (Xrn->r) { \
|
||||
*Xsp++ = Xrn->r; \
|
||||
} \
|
||||
Xrn = Xrn->l; \
|
||||
} else if (Xrn->r) { \
|
||||
Xrn = Xrn->r; \
|
||||
} else if (Xsp != Xstack) { \
|
||||
Xrn = *(--Xsp); \
|
||||
} else { \
|
||||
Xrn = (radix_node_t *) 0; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Local additions */
|
||||
|
||||
prefix_t *prefix_pton(const char *string, long len, prefix_t *prefix, const char **errmsg);
|
||||
prefix_t *prefix_from_blob(unsigned char *blob, int len, int prefixlen, prefix_t *prefix);
|
||||
const char *prefix_addr_ntop(prefix_t *prefix, char *buf, size_t len);
|
||||
const char *prefix_ntop(prefix_t *prefix, char *buf, size_t len);
|
||||
|
||||
#endif /* _RADIX_H */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1999-2000
|
||||
*
|
||||
* The Regents of the University of Michigan ("The Regents") and
|
||||
* Merit Network, Inc. All rights reserved. Redistribution and use
|
||||
* in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of
|
||||
* this software must display the following acknowledgement:
|
||||
*
|
||||
* This product includes software developed by the University of
|
||||
* Michigan, Merit Network, Inc., and their contributors.
|
||||
*
|
||||
* 4. Neither the name of the University, Merit Network, nor the
|
||||
* names of their contributors may be used to endorse or
|
||||
* promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TH E REGENTS
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HO WEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*
|
||||
* Portions Copyright (c) 2004,2005 Damien Miller <djm@mindrot.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: radix.h,v 1.9 2007/10/24 06:03:08 djm Exp $ */
|
||||
|
||||
#ifndef _RADIX_H
|
||||
#define _RADIX_H
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <sys/socket.h>
|
||||
# include <netinet/in.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <netdb.h>
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define snprintf _snprintf
|
||||
typedef unsigned __int8 u_int8_t;
|
||||
typedef unsigned __int16 u_int16_t;
|
||||
typedef unsigned __int32 u_int32_t;
|
||||
const char *inet_ntop(int af, const void *src, char *dst, size_t size);
|
||||
size_t strlcpy(char *dst, const char *src, size_t size);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Originally from MRT include/mrt.h
|
||||
* $MRTId: mrt.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $
|
||||
*/
|
||||
typedef struct _prefix_t {
|
||||
u_int family; /* AF_INET | AF_INET6 */
|
||||
u_int bitlen; /* same as mask? */
|
||||
int ref_count; /* reference count */
|
||||
union {
|
||||
struct in_addr sin;
|
||||
struct in6_addr sin6;
|
||||
} add;
|
||||
} prefix_t;
|
||||
|
||||
void Deref_Prefix(prefix_t *prefix);
|
||||
|
||||
/*
|
||||
* Originally from MRT include/radix.h
|
||||
* $MRTId: radix.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $
|
||||
*/
|
||||
typedef struct _radix_node_t {
|
||||
u_int bit; /* flag if this node used */
|
||||
prefix_t *prefix; /* who we are in radix tree */
|
||||
struct _radix_node_t *l, *r; /* left and right children */
|
||||
struct _radix_node_t *parent; /* may be used */
|
||||
void *data; /* pointer to data */
|
||||
} radix_node_t;
|
||||
|
||||
typedef struct _radix_tree_t {
|
||||
radix_node_t *head;
|
||||
u_int maxbits; /* for IP, 32 bit addresses */
|
||||
int num_active_node; /* for debug purpose */
|
||||
} radix_tree_t;
|
||||
|
||||
/* Type of callback function */
|
||||
typedef void (*rdx_cb_t)(radix_node_t *, void *);
|
||||
|
||||
radix_tree_t *New_Radix(void);
|
||||
void Destroy_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx);
|
||||
radix_node_t *radix_lookup(radix_tree_t *radix, prefix_t *prefix);
|
||||
void radix_remove(radix_tree_t *radix, radix_node_t *node);
|
||||
radix_node_t *radix_search_exact(radix_tree_t *radix, prefix_t *prefix);
|
||||
radix_node_t *radix_search_best(radix_tree_t *radix, prefix_t *prefix);
|
||||
void radix_process(radix_tree_t *radix, rdx_cb_t func, void *cbctx);
|
||||
|
||||
#define RADIX_MAXBITS 128
|
||||
|
||||
#define RADIX_WALK(Xhead, Xnode) \
|
||||
do { \
|
||||
radix_node_t *Xstack[RADIX_MAXBITS+1]; \
|
||||
radix_node_t **Xsp = Xstack; \
|
||||
radix_node_t *Xrn = (Xhead); \
|
||||
while ((Xnode = Xrn)) { \
|
||||
if (Xnode->prefix)
|
||||
|
||||
#define RADIX_WALK_END \
|
||||
if (Xrn->l) { \
|
||||
if (Xrn->r) { \
|
||||
*Xsp++ = Xrn->r; \
|
||||
} \
|
||||
Xrn = Xrn->l; \
|
||||
} else if (Xrn->r) { \
|
||||
Xrn = Xrn->r; \
|
||||
} else if (Xsp != Xstack) { \
|
||||
Xrn = *(--Xsp); \
|
||||
} else { \
|
||||
Xrn = (radix_node_t *) 0; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Local additions */
|
||||
|
||||
prefix_t *prefix_pton(const char *string, long len, prefix_t *prefix, const char **errmsg);
|
||||
prefix_t *prefix_from_blob(unsigned char *blob, int len, int prefixlen, prefix_t *prefix);
|
||||
const char *prefix_addr_ntop(prefix_t *prefix, char *buf, size_t len);
|
||||
const char *prefix_ntop(prefix_t *prefix, char *buf, size_t len);
|
||||
|
||||
#endif /* _RADIX_H */
|
||||
|
||||
|
||||
@@ -1,305 +1,305 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _GENERIC_RBTREE_H
|
||||
#define _GENERIC_RBTREE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct rb_node {
|
||||
unsigned long __rb_parent_color;
|
||||
struct rb_node *rb_right;
|
||||
struct rb_node *rb_left;
|
||||
} __attribute__((aligned(sizeof(long))));
|
||||
/* The alignment might seem pointless, but allegedly CRIS needs it */
|
||||
|
||||
struct rb_root {
|
||||
struct rb_node *rb_node;
|
||||
};
|
||||
|
||||
#define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3))
|
||||
|
||||
#define RB_ROOT \
|
||||
(struct rb_root) \
|
||||
{ \
|
||||
NULL, \
|
||||
}
|
||||
#define rb_entry(ptr, type, member) container_of(ptr, type, member)
|
||||
|
||||
#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL)
|
||||
|
||||
/* 'empty' nodes are nodes that are known not to be inserted in an rbtree */
|
||||
#define RB_EMPTY_NODE(node) ((node)->__rb_parent_color == (unsigned long)(node))
|
||||
#define RB_CLEAR_NODE(node) ((node)->__rb_parent_color = (unsigned long)(node))
|
||||
|
||||
extern void rb_insert_color(struct rb_node *, struct rb_root *);
|
||||
extern void rb_erase(struct rb_node *, struct rb_root *);
|
||||
|
||||
/* Find logical next and previous nodes in a tree */
|
||||
extern struct rb_node *rb_next(const struct rb_node *);
|
||||
extern struct rb_node *rb_prev(const struct rb_node *);
|
||||
extern struct rb_node *rb_first(const struct rb_root *);
|
||||
extern struct rb_node *rb_last(const struct rb_root *);
|
||||
|
||||
/* Postorder iteration - always visit the parent after its children */
|
||||
extern struct rb_node *rb_first_postorder(const struct rb_root *);
|
||||
extern struct rb_node *rb_next_postorder(const struct rb_node *);
|
||||
|
||||
/* Fast replacement of a single node without remove/rebalance/add/rebalance */
|
||||
extern void rb_replace_node(struct rb_node *victim, struct rb_node *new_node, struct rb_root *root);
|
||||
|
||||
static inline void rb_link_node(struct rb_node *node, struct rb_node *parent, struct rb_node **rb_link)
|
||||
{
|
||||
node->__rb_parent_color = (unsigned long)parent;
|
||||
node->rb_left = node->rb_right = NULL;
|
||||
|
||||
*rb_link = node;
|
||||
}
|
||||
|
||||
#define rb_entry_safe(ptr, type, member) \
|
||||
({ \
|
||||
typeof(ptr) ____ptr = (ptr); \
|
||||
____ptr ? rb_entry(____ptr, type, member) : NULL; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Handy for checking that we are not deleting an entry that is
|
||||
* already in a list, found in block/{blk-throttle,cfq-iosched}.c,
|
||||
* probably should be moved to lib/rbtree.c...
|
||||
*/
|
||||
static inline void rb_erase_init(struct rb_node *n, struct rb_root *root)
|
||||
{
|
||||
rb_erase(n, root);
|
||||
RB_CLEAR_NODE(n);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Please note - only struct rb_augment_callbacks and the prototypes for
|
||||
* rb_insert_augmented() and rb_erase_augmented() are intended to be public.
|
||||
* The rest are implementation details you are not expected to depend on.
|
||||
*
|
||||
* See Documentation/rbtree.txt for documentation and samples.
|
||||
*/
|
||||
|
||||
struct rb_augment_callbacks {
|
||||
void (*propagate)(struct rb_node *node, struct rb_node *stop);
|
||||
void (*copy)(struct rb_node *old_node, struct rb_node *new_node);
|
||||
void (*rotate)(struct rb_node *old_node, struct rb_node *new_node);
|
||||
};
|
||||
|
||||
extern void __rb_insert_augmented(struct rb_node *node, struct rb_root *root,
|
||||
void (*augment_rotate)(struct rb_node *old_node, struct rb_node *new_node));
|
||||
/*
|
||||
* Fixup the rbtree and update the augmented information when rebalancing.
|
||||
*
|
||||
* On insertion, the user must update the augmented information on the path
|
||||
* leading to the inserted node, then call rb_link_node() as usual and
|
||||
* rb_augment_inserted() instead of the usual rb_insert_color() call.
|
||||
* If rb_augment_inserted() rebalances the rbtree, it will callback into
|
||||
* a user provided function to update the augmented information on the
|
||||
* affected subtrees.
|
||||
*/
|
||||
static inline void
|
||||
rb_insert_augmented(struct rb_node *node, struct rb_root *root,
|
||||
const struct rb_augment_callbacks *augment)
|
||||
{
|
||||
__rb_insert_augmented(node, root, augment->rotate);
|
||||
}
|
||||
|
||||
#define RB_DECLARE_CALLBACKS(rbstatic, rbname, rbstruct, rbfield, \
|
||||
rbtype, rbaugmented, rbcompute) \
|
||||
static inline void \
|
||||
rbname ## _propagate(struct rb_node *rb, struct rb_node *stop) \
|
||||
{ \
|
||||
while (rb != stop) { \
|
||||
rbstruct *node = rb_entry(rb, rbstruct, rbfield); \
|
||||
rbtype augmented = rbcompute(node); \
|
||||
if (node->rbaugmented == augmented) \
|
||||
break; \
|
||||
node->rbaugmented = augmented; \
|
||||
rb = rb_parent(&node->rbfield); \
|
||||
} \
|
||||
} \
|
||||
static inline void \
|
||||
rbname ## _copy(struct rb_node *rb_old, struct rb_node *rb_new) \
|
||||
{ \
|
||||
rbstruct *old_node = rb_entry(rb_old, rbstruct, rbfield); \
|
||||
rbstruct *new_node = rb_entry(rb_new, rbstruct, rbfield); \
|
||||
new_node->rbaugmented = old_node->rbaugmented; \
|
||||
} \
|
||||
static void \
|
||||
rbname ## _rotate(struct rb_node *rb_old, struct rb_node *rb_new) \
|
||||
{ \
|
||||
rbstruct *old_node = rb_entry(rb_old, rbstruct, rbfield); \
|
||||
rbstruct *new_node = rb_entry(rb_new, rbstruct, rbfield); \
|
||||
new_node->rbaugmented = old_node->rbaugmented; \
|
||||
old_node->rbaugmented = rbcompute(old_node); \
|
||||
} \
|
||||
rbstatic const struct rb_augment_callbacks rbname = { \
|
||||
rbname ## _propagate, rbname ## _copy, rbname ## _rotate \
|
||||
};
|
||||
|
||||
|
||||
#define RB_RED 0
|
||||
#define RB_BLACK 1
|
||||
|
||||
#define __rb_parent(pc) ((struct rb_node *)(pc & ~3))
|
||||
|
||||
#define __rb_color(pc) ((pc) & 1)
|
||||
#define __rb_is_black(pc) __rb_color(pc)
|
||||
#define __rb_is_red(pc) (!__rb_color(pc))
|
||||
#define rb_color(rb) __rb_color((rb)->__rb_parent_color)
|
||||
#define rb_is_red(rb) __rb_is_red((rb)->__rb_parent_color)
|
||||
#define rb_is_black(rb) __rb_is_black((rb)->__rb_parent_color)
|
||||
|
||||
static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
|
||||
{
|
||||
rb->__rb_parent_color = rb_color(rb) | (unsigned long)p;
|
||||
}
|
||||
|
||||
static inline void rb_set_parent_color(struct rb_node *rb,
|
||||
struct rb_node *p, int color)
|
||||
{
|
||||
rb->__rb_parent_color = (unsigned long)p | color;
|
||||
}
|
||||
|
||||
static inline void
|
||||
__rb_change_child(struct rb_node *old_node, struct rb_node *new_node,
|
||||
struct rb_node *parent, struct rb_root *root)
|
||||
{
|
||||
if (parent) {
|
||||
if (parent->rb_left == old_node)
|
||||
parent->rb_left = new_node;
|
||||
else
|
||||
parent->rb_right = new_node;
|
||||
} else
|
||||
root->rb_node = new_node;
|
||||
}
|
||||
|
||||
extern void __rb_erase_color(struct rb_node *parent, struct rb_root *root,
|
||||
void (*augment_rotate)(struct rb_node *old_node, struct rb_node *new_node));
|
||||
|
||||
static inline struct rb_node *
|
||||
__rb_erase_augmented(struct rb_node *node, struct rb_root *root,
|
||||
const struct rb_augment_callbacks *augment)
|
||||
{
|
||||
struct rb_node *child = node->rb_right, *tmp = node->rb_left;
|
||||
struct rb_node *parent, *rebalance;
|
||||
unsigned long pc;
|
||||
|
||||
if (!tmp) {
|
||||
/*
|
||||
* Case 1: node to erase has no more than 1 child (easy!)
|
||||
*
|
||||
* Note that if there is one child it must be red due to 5)
|
||||
* and node must be black due to 4). We adjust colors locally
|
||||
* so as to bypass __rb_erase_color() later on.
|
||||
*/
|
||||
pc = node->__rb_parent_color;
|
||||
parent = __rb_parent(pc);
|
||||
__rb_change_child(node, child, parent, root);
|
||||
if (child) {
|
||||
child->__rb_parent_color = pc;
|
||||
rebalance = NULL;
|
||||
} else
|
||||
rebalance = __rb_is_black(pc) ? parent : NULL;
|
||||
tmp = parent;
|
||||
} else if (!child) {
|
||||
/* Still case 1, but this time the child is node->rb_left */
|
||||
tmp->__rb_parent_color = pc = node->__rb_parent_color;
|
||||
parent = __rb_parent(pc);
|
||||
__rb_change_child(node, tmp, parent, root);
|
||||
rebalance = NULL;
|
||||
tmp = parent;
|
||||
} else {
|
||||
struct rb_node *successor = child, *child2;
|
||||
tmp = child->rb_left;
|
||||
if (!tmp) {
|
||||
/*
|
||||
* Case 2: node's successor is its right child
|
||||
*
|
||||
* (n) (s)
|
||||
* / \ / \
|
||||
* (x) (s) -> (x) (c)
|
||||
* \
|
||||
* (c)
|
||||
*/
|
||||
parent = successor;
|
||||
child2 = successor->rb_right;
|
||||
augment->copy(node, successor);
|
||||
} else {
|
||||
/*
|
||||
* Case 3: node's successor is leftmost under
|
||||
* node's right child subtree
|
||||
*
|
||||
* (n) (s)
|
||||
* / \ / \
|
||||
* (x) (y) -> (x) (y)
|
||||
* / /
|
||||
* (p) (p)
|
||||
* / /
|
||||
* (s) (c)
|
||||
* \
|
||||
* (c)
|
||||
*/
|
||||
do {
|
||||
parent = successor;
|
||||
successor = tmp;
|
||||
tmp = tmp->rb_left;
|
||||
} while (tmp);
|
||||
parent->rb_left = child2 = successor->rb_right;
|
||||
successor->rb_right = child;
|
||||
rb_set_parent(child, successor);
|
||||
augment->copy(node, successor);
|
||||
augment->propagate(parent, successor);
|
||||
}
|
||||
|
||||
successor->rb_left = tmp = node->rb_left;
|
||||
rb_set_parent(tmp, successor);
|
||||
|
||||
pc = node->__rb_parent_color;
|
||||
tmp = __rb_parent(pc);
|
||||
__rb_change_child(node, successor, tmp, root);
|
||||
if (child2) {
|
||||
successor->__rb_parent_color = pc;
|
||||
rb_set_parent_color(child2, parent, RB_BLACK);
|
||||
rebalance = NULL;
|
||||
} else {
|
||||
unsigned long pc2 = successor->__rb_parent_color;
|
||||
successor->__rb_parent_color = pc;
|
||||
rebalance = __rb_is_black(pc2) ? parent : NULL;
|
||||
}
|
||||
tmp = successor;
|
||||
}
|
||||
|
||||
augment->propagate(tmp, NULL);
|
||||
return rebalance;
|
||||
}
|
||||
|
||||
static inline void
|
||||
rb_erase_augmented(struct rb_node *node, struct rb_root *root,
|
||||
const struct rb_augment_callbacks *augment)
|
||||
{
|
||||
struct rb_node *rebalance = __rb_erase_augmented(node, root, augment);
|
||||
if (rebalance)
|
||||
__rb_erase_color(rebalance, root, augment->rotate);
|
||||
}
|
||||
|
||||
#endif /* _GENERIC_RBTREE_H */
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _GENERIC_RBTREE_H
|
||||
#define _GENERIC_RBTREE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct rb_node {
|
||||
unsigned long __rb_parent_color;
|
||||
struct rb_node *rb_right;
|
||||
struct rb_node *rb_left;
|
||||
} __attribute__((aligned(sizeof(long))));
|
||||
/* The alignment might seem pointless, but allegedly CRIS needs it */
|
||||
|
||||
struct rb_root {
|
||||
struct rb_node *rb_node;
|
||||
};
|
||||
|
||||
#define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3))
|
||||
|
||||
#define RB_ROOT \
|
||||
(struct rb_root) \
|
||||
{ \
|
||||
NULL, \
|
||||
}
|
||||
#define rb_entry(ptr, type, member) container_of(ptr, type, member)
|
||||
|
||||
#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL)
|
||||
|
||||
/* 'empty' nodes are nodes that are known not to be inserted in an rbtree */
|
||||
#define RB_EMPTY_NODE(node) ((node)->__rb_parent_color == (unsigned long)(node))
|
||||
#define RB_CLEAR_NODE(node) ((node)->__rb_parent_color = (unsigned long)(node))
|
||||
|
||||
extern void rb_insert_color(struct rb_node *, struct rb_root *);
|
||||
extern void rb_erase(struct rb_node *, struct rb_root *);
|
||||
|
||||
/* Find logical next and previous nodes in a tree */
|
||||
extern struct rb_node *rb_next(const struct rb_node *);
|
||||
extern struct rb_node *rb_prev(const struct rb_node *);
|
||||
extern struct rb_node *rb_first(const struct rb_root *);
|
||||
extern struct rb_node *rb_last(const struct rb_root *);
|
||||
|
||||
/* Postorder iteration - always visit the parent after its children */
|
||||
extern struct rb_node *rb_first_postorder(const struct rb_root *);
|
||||
extern struct rb_node *rb_next_postorder(const struct rb_node *);
|
||||
|
||||
/* Fast replacement of a single node without remove/rebalance/add/rebalance */
|
||||
extern void rb_replace_node(struct rb_node *victim, struct rb_node *new_node, struct rb_root *root);
|
||||
|
||||
static inline void rb_link_node(struct rb_node *node, struct rb_node *parent, struct rb_node **rb_link)
|
||||
{
|
||||
node->__rb_parent_color = (unsigned long)parent;
|
||||
node->rb_left = node->rb_right = NULL;
|
||||
|
||||
*rb_link = node;
|
||||
}
|
||||
|
||||
#define rb_entry_safe(ptr, type, member) \
|
||||
({ \
|
||||
typeof(ptr) ____ptr = (ptr); \
|
||||
____ptr ? rb_entry(____ptr, type, member) : NULL; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Handy for checking that we are not deleting an entry that is
|
||||
* already in a list, found in block/{blk-throttle,cfq-iosched}.c,
|
||||
* probably should be moved to lib/rbtree.c...
|
||||
*/
|
||||
static inline void rb_erase_init(struct rb_node *n, struct rb_root *root)
|
||||
{
|
||||
rb_erase(n, root);
|
||||
RB_CLEAR_NODE(n);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Please note - only struct rb_augment_callbacks and the prototypes for
|
||||
* rb_insert_augmented() and rb_erase_augmented() are intended to be public.
|
||||
* The rest are implementation details you are not expected to depend on.
|
||||
*
|
||||
* See Documentation/rbtree.txt for documentation and samples.
|
||||
*/
|
||||
|
||||
struct rb_augment_callbacks {
|
||||
void (*propagate)(struct rb_node *node, struct rb_node *stop);
|
||||
void (*copy)(struct rb_node *old_node, struct rb_node *new_node);
|
||||
void (*rotate)(struct rb_node *old_node, struct rb_node *new_node);
|
||||
};
|
||||
|
||||
extern void __rb_insert_augmented(struct rb_node *node, struct rb_root *root,
|
||||
void (*augment_rotate)(struct rb_node *old_node, struct rb_node *new_node));
|
||||
/*
|
||||
* Fixup the rbtree and update the augmented information when rebalancing.
|
||||
*
|
||||
* On insertion, the user must update the augmented information on the path
|
||||
* leading to the inserted node, then call rb_link_node() as usual and
|
||||
* rb_augment_inserted() instead of the usual rb_insert_color() call.
|
||||
* If rb_augment_inserted() rebalances the rbtree, it will callback into
|
||||
* a user provided function to update the augmented information on the
|
||||
* affected subtrees.
|
||||
*/
|
||||
static inline void
|
||||
rb_insert_augmented(struct rb_node *node, struct rb_root *root,
|
||||
const struct rb_augment_callbacks *augment)
|
||||
{
|
||||
__rb_insert_augmented(node, root, augment->rotate);
|
||||
}
|
||||
|
||||
#define RB_DECLARE_CALLBACKS(rbstatic, rbname, rbstruct, rbfield, \
|
||||
rbtype, rbaugmented, rbcompute) \
|
||||
static inline void \
|
||||
rbname ## _propagate(struct rb_node *rb, struct rb_node *stop) \
|
||||
{ \
|
||||
while (rb != stop) { \
|
||||
rbstruct *node = rb_entry(rb, rbstruct, rbfield); \
|
||||
rbtype augmented = rbcompute(node); \
|
||||
if (node->rbaugmented == augmented) \
|
||||
break; \
|
||||
node->rbaugmented = augmented; \
|
||||
rb = rb_parent(&node->rbfield); \
|
||||
} \
|
||||
} \
|
||||
static inline void \
|
||||
rbname ## _copy(struct rb_node *rb_old, struct rb_node *rb_new) \
|
||||
{ \
|
||||
rbstruct *old_node = rb_entry(rb_old, rbstruct, rbfield); \
|
||||
rbstruct *new_node = rb_entry(rb_new, rbstruct, rbfield); \
|
||||
new_node->rbaugmented = old_node->rbaugmented; \
|
||||
} \
|
||||
static void \
|
||||
rbname ## _rotate(struct rb_node *rb_old, struct rb_node *rb_new) \
|
||||
{ \
|
||||
rbstruct *old_node = rb_entry(rb_old, rbstruct, rbfield); \
|
||||
rbstruct *new_node = rb_entry(rb_new, rbstruct, rbfield); \
|
||||
new_node->rbaugmented = old_node->rbaugmented; \
|
||||
old_node->rbaugmented = rbcompute(old_node); \
|
||||
} \
|
||||
rbstatic const struct rb_augment_callbacks rbname = { \
|
||||
rbname ## _propagate, rbname ## _copy, rbname ## _rotate \
|
||||
};
|
||||
|
||||
|
||||
#define RB_RED 0
|
||||
#define RB_BLACK 1
|
||||
|
||||
#define __rb_parent(pc) ((struct rb_node *)(pc & ~3))
|
||||
|
||||
#define __rb_color(pc) ((pc) & 1)
|
||||
#define __rb_is_black(pc) __rb_color(pc)
|
||||
#define __rb_is_red(pc) (!__rb_color(pc))
|
||||
#define rb_color(rb) __rb_color((rb)->__rb_parent_color)
|
||||
#define rb_is_red(rb) __rb_is_red((rb)->__rb_parent_color)
|
||||
#define rb_is_black(rb) __rb_is_black((rb)->__rb_parent_color)
|
||||
|
||||
static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
|
||||
{
|
||||
rb->__rb_parent_color = rb_color(rb) | (unsigned long)p;
|
||||
}
|
||||
|
||||
static inline void rb_set_parent_color(struct rb_node *rb,
|
||||
struct rb_node *p, int color)
|
||||
{
|
||||
rb->__rb_parent_color = (unsigned long)p | color;
|
||||
}
|
||||
|
||||
static inline void
|
||||
__rb_change_child(struct rb_node *old_node, struct rb_node *new_node,
|
||||
struct rb_node *parent, struct rb_root *root)
|
||||
{
|
||||
if (parent) {
|
||||
if (parent->rb_left == old_node)
|
||||
parent->rb_left = new_node;
|
||||
else
|
||||
parent->rb_right = new_node;
|
||||
} else
|
||||
root->rb_node = new_node;
|
||||
}
|
||||
|
||||
extern void __rb_erase_color(struct rb_node *parent, struct rb_root *root,
|
||||
void (*augment_rotate)(struct rb_node *old_node, struct rb_node *new_node));
|
||||
|
||||
static inline struct rb_node *
|
||||
__rb_erase_augmented(struct rb_node *node, struct rb_root *root,
|
||||
const struct rb_augment_callbacks *augment)
|
||||
{
|
||||
struct rb_node *child = node->rb_right, *tmp = node->rb_left;
|
||||
struct rb_node *parent, *rebalance;
|
||||
unsigned long pc;
|
||||
|
||||
if (!tmp) {
|
||||
/*
|
||||
* Case 1: node to erase has no more than 1 child (easy!)
|
||||
*
|
||||
* Note that if there is one child it must be red due to 5)
|
||||
* and node must be black due to 4). We adjust colors locally
|
||||
* so as to bypass __rb_erase_color() later on.
|
||||
*/
|
||||
pc = node->__rb_parent_color;
|
||||
parent = __rb_parent(pc);
|
||||
__rb_change_child(node, child, parent, root);
|
||||
if (child) {
|
||||
child->__rb_parent_color = pc;
|
||||
rebalance = NULL;
|
||||
} else
|
||||
rebalance = __rb_is_black(pc) ? parent : NULL;
|
||||
tmp = parent;
|
||||
} else if (!child) {
|
||||
/* Still case 1, but this time the child is node->rb_left */
|
||||
tmp->__rb_parent_color = pc = node->__rb_parent_color;
|
||||
parent = __rb_parent(pc);
|
||||
__rb_change_child(node, tmp, parent, root);
|
||||
rebalance = NULL;
|
||||
tmp = parent;
|
||||
} else {
|
||||
struct rb_node *successor = child, *child2;
|
||||
tmp = child->rb_left;
|
||||
if (!tmp) {
|
||||
/*
|
||||
* Case 2: node's successor is its right child
|
||||
*
|
||||
* (n) (s)
|
||||
* / \ / \
|
||||
* (x) (s) -> (x) (c)
|
||||
* \
|
||||
* (c)
|
||||
*/
|
||||
parent = successor;
|
||||
child2 = successor->rb_right;
|
||||
augment->copy(node, successor);
|
||||
} else {
|
||||
/*
|
||||
* Case 3: node's successor is leftmost under
|
||||
* node's right child subtree
|
||||
*
|
||||
* (n) (s)
|
||||
* / \ / \
|
||||
* (x) (y) -> (x) (y)
|
||||
* / /
|
||||
* (p) (p)
|
||||
* / /
|
||||
* (s) (c)
|
||||
* \
|
||||
* (c)
|
||||
*/
|
||||
do {
|
||||
parent = successor;
|
||||
successor = tmp;
|
||||
tmp = tmp->rb_left;
|
||||
} while (tmp);
|
||||
parent->rb_left = child2 = successor->rb_right;
|
||||
successor->rb_right = child;
|
||||
rb_set_parent(child, successor);
|
||||
augment->copy(node, successor);
|
||||
augment->propagate(parent, successor);
|
||||
}
|
||||
|
||||
successor->rb_left = tmp = node->rb_left;
|
||||
rb_set_parent(tmp, successor);
|
||||
|
||||
pc = node->__rb_parent_color;
|
||||
tmp = __rb_parent(pc);
|
||||
__rb_change_child(node, successor, tmp, root);
|
||||
if (child2) {
|
||||
successor->__rb_parent_color = pc;
|
||||
rb_set_parent_color(child2, parent, RB_BLACK);
|
||||
rebalance = NULL;
|
||||
} else {
|
||||
unsigned long pc2 = successor->__rb_parent_color;
|
||||
successor->__rb_parent_color = pc;
|
||||
rebalance = __rb_is_black(pc2) ? parent : NULL;
|
||||
}
|
||||
tmp = successor;
|
||||
}
|
||||
|
||||
augment->propagate(tmp, NULL);
|
||||
return rebalance;
|
||||
}
|
||||
|
||||
static inline void
|
||||
rb_erase_augmented(struct rb_node *node, struct rb_root *root,
|
||||
const struct rb_augment_callbacks *augment)
|
||||
{
|
||||
struct rb_node *rebalance = __rb_erase_augmented(node, root, augment);
|
||||
if (rebalance)
|
||||
__rb_erase_color(rebalance, root, augment->rotate);
|
||||
}
|
||||
|
||||
#endif /* _GENERIC_RBTREE_H */
|
||||
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _GENERIC_STRING_UITL_H
|
||||
#define _GENERIC_STRING_UITL_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
static inline char *safe_strncpy(char *dest, const char *src, size_t n)
|
||||
{
|
||||
#if __GNUC__ > 7
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstringop-truncation"
|
||||
#endif
|
||||
char *ret = strncpy(dest, src, n - 1);
|
||||
if (n > 0) {
|
||||
dest[n - 1] = '\0';
|
||||
}
|
||||
#if __GNUC__ > 7
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _GENERIC_STRING_UITL_H
|
||||
#define _GENERIC_STRING_UITL_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
static inline char *safe_strncpy(char *dest, const char *src, size_t n)
|
||||
{
|
||||
#if __GNUC__ > 7
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstringop-truncation"
|
||||
#endif
|
||||
char *ret = strncpy(dest, src, n - 1);
|
||||
if (n > 0) {
|
||||
dest[n - 1] = '\0';
|
||||
}
|
||||
#if __GNUC__ > 7
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
2258
src/lib/art.c
2258
src/lib/art.c
File diff suppressed because it is too large
Load Diff
622
src/lib/conf.c
622
src/lib/conf.c
@@ -1,311 +1,311 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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 "conf.h"
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const char *currrent_conf_file = NULL;
|
||||
|
||||
const char *conf_get_conf_file(void)
|
||||
{
|
||||
return currrent_conf_file;
|
||||
}
|
||||
|
||||
int conf_custom(const char *item, void *data, int argc, char *argv[])
|
||||
{
|
||||
struct config_item_custom *item_custom = data;
|
||||
return item_custom->custom_func(item_custom->custom_data, argc, argv);
|
||||
}
|
||||
|
||||
int conf_int(const char *item, void *data, int argc, char *argv[])
|
||||
{
|
||||
struct config_item_int *item_int = data;
|
||||
int value = 0;
|
||||
if (argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
value = atoi(argv[1]);
|
||||
|
||||
if (value < item_int->min) {
|
||||
value = item_int->min;
|
||||
} else if (value > item_int->max) {
|
||||
value = item_int->max;
|
||||
}
|
||||
|
||||
*(item_int->data) = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conf_string(const char *item, void *data, int argc, char *argv[])
|
||||
{
|
||||
struct config_item_string *item_string = data;
|
||||
|
||||
if (argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(item_string->data, argv[1], item_string->size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conf_yesno(const char *item, void *data, int argc, char *argv[])
|
||||
{
|
||||
struct config_item_yesno *item_yesno = data;
|
||||
int yes = 0;
|
||||
|
||||
if (argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *value = argv[1];
|
||||
if (strncmp("auto", value, sizeof("auto")) == 0 || strncmp("AUTO", value, sizeof("AUTO")) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strncmp("yes", value, sizeof("yes")) == 0 || strncmp("YES", value, sizeof("YES")) == 0) {
|
||||
yes = 1;
|
||||
} else if (strncmp("no", value, sizeof("no")) == 0 || strncmp("NO", value, sizeof("NO")) == 0) {
|
||||
yes = 0;
|
||||
}
|
||||
|
||||
*(item_yesno->data) = yes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conf_size(const char *item, void *data, int argc, char *argv[])
|
||||
{
|
||||
/* read dns cache size */
|
||||
int base = 1;
|
||||
size_t size = 0;
|
||||
int num = 0;
|
||||
struct config_item_size *item_size = data;
|
||||
char *value = argv[1];
|
||||
|
||||
if (strstr(value, "k") || strstr(value, "K")) {
|
||||
base = 1024;
|
||||
} else if (strstr(value, "m") || strstr(value, "M")) {
|
||||
base = 1024 * 1024;
|
||||
} else if (strstr(value, "g") || strstr(value, "G")) {
|
||||
base = 1024 * 1024 * 1024;
|
||||
}
|
||||
|
||||
num = atoi(value);
|
||||
if (num < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size = num * base;
|
||||
if (size > item_size->max) {
|
||||
size = item_size->max;
|
||||
} else if (size < item_size->min) {
|
||||
size = item_size->min;
|
||||
}
|
||||
|
||||
*(item_size->data) = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void conf_getopt_reset(void)
|
||||
{
|
||||
static struct option long_options[] = {{"-", 0, 0, 0}, {0, 0, 0, 0}};
|
||||
int argc = 2;
|
||||
char *argv[3] = {"reset", "", 0};
|
||||
|
||||
optind = 0;
|
||||
opterr = 0;
|
||||
optopt = 0;
|
||||
getopt_long(argc, argv, "", long_options, NULL);
|
||||
optind = 0;
|
||||
opterr = 0;
|
||||
optopt = 0;
|
||||
}
|
||||
|
||||
int conf_parse_args(char *key, char *value, int *argc, char **argv)
|
||||
{
|
||||
char *start = NULL;
|
||||
char *ptr = value;
|
||||
int count = 0;
|
||||
int sep_flag = ' ';
|
||||
|
||||
argv[0] = key;
|
||||
count++;
|
||||
|
||||
while (*ptr != '\0') {
|
||||
if (*ptr == '\\') {
|
||||
char *tmp = ptr + 1;
|
||||
while (*tmp != '\0') {
|
||||
*(tmp - 1) = *tmp;
|
||||
tmp++;
|
||||
}
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*ptr == '"' && start == NULL) {
|
||||
sep_flag = '"';
|
||||
start = NULL;
|
||||
}
|
||||
|
||||
if (*ptr != sep_flag && start == NULL) {
|
||||
start = ptr;
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*ptr == sep_flag && start == NULL) {
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*ptr == sep_flag && start) {
|
||||
argv[count] = start;
|
||||
*ptr = '\0';
|
||||
ptr++;
|
||||
count++;
|
||||
sep_flag = ' ';
|
||||
start = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (start != ptr && start) {
|
||||
argv[count] = start;
|
||||
count++;
|
||||
}
|
||||
|
||||
*argc = count;
|
||||
argv[count] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void load_exit(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int load_conf_printf(const char *file, int lineno, int ret)
|
||||
{
|
||||
if (ret != CONF_RET_OK) {
|
||||
printf("process config file '%s' failed at line %d.", file, lineno);
|
||||
if (ret == CONF_RET_ERR || ret == CONF_RET_NOENT) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int load_conf_file(const char *file, struct config_item *items, conf_error_handler handler)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
char line[MAX_LINE_LEN];
|
||||
char key[MAX_KEY_LEN];
|
||||
char value[MAX_LINE_LEN];
|
||||
int filed_num = 0;
|
||||
int i;
|
||||
int argc;
|
||||
char *argv[1024];
|
||||
int ret = 0;
|
||||
int call_ret = 0;
|
||||
int line_no = 0;
|
||||
|
||||
if (handler == NULL) {
|
||||
handler = load_conf_printf;
|
||||
}
|
||||
|
||||
fp = fopen(file, "r");
|
||||
if (fp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
line_no = 0;
|
||||
while (fgets(line, MAX_LINE_LEN, fp)) {
|
||||
line_no++;
|
||||
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++) {
|
||||
if (items[i].item == NULL) {
|
||||
handler(file, line_no, CONF_RET_NOENT);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strncmp(items[i].item, key, MAX_KEY_LEN) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (conf_parse_args(key, value, &argc, argv) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
conf_getopt_reset();
|
||||
/* call item function */
|
||||
currrent_conf_file = file;
|
||||
call_ret = items[i].item_func(items[i].item, items[i].data, argc, argv);
|
||||
ret = handler(file, line_no, call_ret);
|
||||
if (ret != 0) {
|
||||
conf_getopt_reset();
|
||||
goto errout;
|
||||
}
|
||||
|
||||
conf_getopt_reset();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
errout:
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int load_conf(const char *file, struct config_item items[], conf_error_handler handler)
|
||||
{
|
||||
return load_conf_file(file, items, handler);
|
||||
}
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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 "conf.h"
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const char *currrent_conf_file = NULL;
|
||||
|
||||
const char *conf_get_conf_file(void)
|
||||
{
|
||||
return currrent_conf_file;
|
||||
}
|
||||
|
||||
int conf_custom(const char *item, void *data, int argc, char *argv[])
|
||||
{
|
||||
struct config_item_custom *item_custom = data;
|
||||
return item_custom->custom_func(item_custom->custom_data, argc, argv);
|
||||
}
|
||||
|
||||
int conf_int(const char *item, void *data, int argc, char *argv[])
|
||||
{
|
||||
struct config_item_int *item_int = data;
|
||||
int value = 0;
|
||||
if (argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
value = atoi(argv[1]);
|
||||
|
||||
if (value < item_int->min) {
|
||||
value = item_int->min;
|
||||
} else if (value > item_int->max) {
|
||||
value = item_int->max;
|
||||
}
|
||||
|
||||
*(item_int->data) = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conf_string(const char *item, void *data, int argc, char *argv[])
|
||||
{
|
||||
struct config_item_string *item_string = data;
|
||||
|
||||
if (argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(item_string->data, argv[1], item_string->size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conf_yesno(const char *item, void *data, int argc, char *argv[])
|
||||
{
|
||||
struct config_item_yesno *item_yesno = data;
|
||||
int yes = 0;
|
||||
|
||||
if (argc < 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *value = argv[1];
|
||||
if (strncmp("auto", value, sizeof("auto")) == 0 || strncmp("AUTO", value, sizeof("AUTO")) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strncmp("yes", value, sizeof("yes")) == 0 || strncmp("YES", value, sizeof("YES")) == 0) {
|
||||
yes = 1;
|
||||
} else if (strncmp("no", value, sizeof("no")) == 0 || strncmp("NO", value, sizeof("NO")) == 0) {
|
||||
yes = 0;
|
||||
}
|
||||
|
||||
*(item_yesno->data) = yes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conf_size(const char *item, void *data, int argc, char *argv[])
|
||||
{
|
||||
/* read dns cache size */
|
||||
int base = 1;
|
||||
size_t size = 0;
|
||||
int num = 0;
|
||||
struct config_item_size *item_size = data;
|
||||
char *value = argv[1];
|
||||
|
||||
if (strstr(value, "k") || strstr(value, "K")) {
|
||||
base = 1024;
|
||||
} else if (strstr(value, "m") || strstr(value, "M")) {
|
||||
base = 1024 * 1024;
|
||||
} else if (strstr(value, "g") || strstr(value, "G")) {
|
||||
base = 1024 * 1024 * 1024;
|
||||
}
|
||||
|
||||
num = atoi(value);
|
||||
if (num < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size = num * base;
|
||||
if (size > item_size->max) {
|
||||
size = item_size->max;
|
||||
} else if (size < item_size->min) {
|
||||
size = item_size->min;
|
||||
}
|
||||
|
||||
*(item_size->data) = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void conf_getopt_reset(void)
|
||||
{
|
||||
static struct option long_options[] = {{"-", 0, 0, 0}, {0, 0, 0, 0}};
|
||||
int argc = 2;
|
||||
char *argv[3] = {"reset", "", 0};
|
||||
|
||||
optind = 0;
|
||||
opterr = 0;
|
||||
optopt = 0;
|
||||
getopt_long(argc, argv, "", long_options, NULL);
|
||||
optind = 0;
|
||||
opterr = 0;
|
||||
optopt = 0;
|
||||
}
|
||||
|
||||
int conf_parse_args(char *key, char *value, int *argc, char **argv)
|
||||
{
|
||||
char *start = NULL;
|
||||
char *ptr = value;
|
||||
int count = 0;
|
||||
int sep_flag = ' ';
|
||||
|
||||
argv[0] = key;
|
||||
count++;
|
||||
|
||||
while (*ptr != '\0') {
|
||||
if (*ptr == '\\') {
|
||||
char *tmp = ptr + 1;
|
||||
while (*tmp != '\0') {
|
||||
*(tmp - 1) = *tmp;
|
||||
tmp++;
|
||||
}
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*ptr == '"' && start == NULL) {
|
||||
sep_flag = '"';
|
||||
start = NULL;
|
||||
}
|
||||
|
||||
if (*ptr != sep_flag && start == NULL) {
|
||||
start = ptr;
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*ptr == sep_flag && start == NULL) {
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*ptr == sep_flag && start) {
|
||||
argv[count] = start;
|
||||
*ptr = '\0';
|
||||
ptr++;
|
||||
count++;
|
||||
sep_flag = ' ';
|
||||
start = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (start != ptr && start) {
|
||||
argv[count] = start;
|
||||
count++;
|
||||
}
|
||||
|
||||
*argc = count;
|
||||
argv[count] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void load_exit(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int load_conf_printf(const char *file, int lineno, int ret)
|
||||
{
|
||||
if (ret != CONF_RET_OK) {
|
||||
printf("process config file '%s' failed at line %d.", file, lineno);
|
||||
if (ret == CONF_RET_ERR || ret == CONF_RET_NOENT) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int load_conf_file(const char *file, struct config_item *items, conf_error_handler handler)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
char line[MAX_LINE_LEN];
|
||||
char key[MAX_KEY_LEN];
|
||||
char value[MAX_LINE_LEN];
|
||||
int filed_num = 0;
|
||||
int i;
|
||||
int argc;
|
||||
char *argv[1024];
|
||||
int ret = 0;
|
||||
int call_ret = 0;
|
||||
int line_no = 0;
|
||||
|
||||
if (handler == NULL) {
|
||||
handler = load_conf_printf;
|
||||
}
|
||||
|
||||
fp = fopen(file, "r");
|
||||
if (fp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
line_no = 0;
|
||||
while (fgets(line, MAX_LINE_LEN, fp)) {
|
||||
line_no++;
|
||||
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++) {
|
||||
if (items[i].item == NULL) {
|
||||
handler(file, line_no, CONF_RET_NOENT);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strncmp(items[i].item, key, MAX_KEY_LEN) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (conf_parse_args(key, value, &argc, argv) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
conf_getopt_reset();
|
||||
/* call item function */
|
||||
currrent_conf_file = file;
|
||||
call_ret = items[i].item_func(items[i].item, items[i].data, argc, argv);
|
||||
ret = handler(file, line_no, call_ret);
|
||||
if (ret != 0) {
|
||||
conf_getopt_reset();
|
||||
goto errout;
|
||||
}
|
||||
|
||||
conf_getopt_reset();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
errout:
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int load_conf(const char *file, struct config_item items[], conf_error_handler handler)
|
||||
{
|
||||
return load_conf_file(file, items, handler);
|
||||
}
|
||||
|
||||
1352
src/lib/radix.c
1352
src/lib/radix.c
File diff suppressed because it is too large
Load Diff
1048
src/lib/rbtree.c
1048
src/lib/rbtree.c
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,17 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
886
src/smartdns.c
886
src/smartdns.c
@@ -1,443 +1,443 @@
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include "art.h"
|
||||
#include "atomic.h"
|
||||
#include "dns_client.h"
|
||||
#include "dns_conf.h"
|
||||
#include "dns_server.h"
|
||||
#include "fast_ping.h"
|
||||
#include "hashtable.h"
|
||||
#include "list.h"
|
||||
#include "rbtree.h"
|
||||
#include "tlog.h"
|
||||
#include "util.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#define RESOLVE_FILE "/etc/resolv.conf"
|
||||
#define MAX_LINE_LEN 1024
|
||||
#define MAX_KEY_LEN 64
|
||||
#define SMARTDNS_PID_FILE "/var/run/smartdns.pid"
|
||||
#define TMP_BUFF_LEN_32 32
|
||||
|
||||
static int verbose_screen;
|
||||
|
||||
static 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"
|
||||
" -p [pid] pid file path\n"
|
||||
" -S ignore segment fault signal.\n"
|
||||
" -x verbose screen.\n"
|
||||
" -v dispaly version.\n"
|
||||
" -h show this help message.\n"
|
||||
|
||||
"Online help: http://pymumu.github.io/smartdns\n"
|
||||
"Copyright (C) Nick Peng <pymumu@gmail.com>\n"
|
||||
;
|
||||
/* clang-format on */
|
||||
printf("%s", help);
|
||||
}
|
||||
|
||||
static void _show_version(void)
|
||||
{
|
||||
char str_ver[256] = {0};
|
||||
#ifdef SMARTDNS_VERION
|
||||
const char *ver = SMARTDNS_VERION;
|
||||
snprintf(str_ver, sizeof(str_ver), "%s", ver);
|
||||
#else
|
||||
struct tm tm;
|
||||
get_compiled_time(&tm);
|
||||
snprintf(str_ver, sizeof(str_ver), "1.%.4d%.2d%.2d-%.2d%.2d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
|
||||
#endif
|
||||
printf("smartdns %s\n", str_ver);
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
safe_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;
|
||||
}
|
||||
|
||||
static int _smartdns_add_servers(void)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int ret = 0;
|
||||
struct dns_server_groups *group = NULL;
|
||||
struct dns_servers *server = NULL;
|
||||
struct client_dns_server_flags flags;
|
||||
|
||||
for (i = 0; i < dns_conf_server_num; i++) {
|
||||
memset(&flags, 0, sizeof(flags));
|
||||
switch (dns_conf_servers[i].type) {
|
||||
case DNS_SERVER_UDP: {
|
||||
struct client_dns_server_flag_udp *flag_udp = &flags.udp;
|
||||
flag_udp->ttl = dns_conf_servers[i].ttl;
|
||||
} break;
|
||||
case DNS_SERVER_HTTPS: {
|
||||
struct client_dns_server_flag_https *flag_http = &flags.https;
|
||||
flag_http->spi_len = dns_client_spki_decode(dns_conf_servers[i].spki, (unsigned char *)flag_http->spki);
|
||||
safe_strncpy(flag_http->hostname, dns_conf_servers[i].hostname, sizeof(flag_http->hostname));
|
||||
safe_strncpy(flag_http->path, dns_conf_servers[i].path, sizeof(flag_http->path));
|
||||
safe_strncpy(flag_http->httphost, dns_conf_servers[i].httphost, sizeof(flag_http->httphost));
|
||||
safe_strncpy(flag_http->tls_host_verify, dns_conf_servers[i].tls_host_verify, sizeof(flag_http->tls_host_verify));
|
||||
} break;
|
||||
case DNS_SERVER_TLS: {
|
||||
struct client_dns_server_flag_tls *flag_tls = &flags.tls;
|
||||
flag_tls->spi_len = dns_client_spki_decode(dns_conf_servers[i].spki, (unsigned char *)flag_tls->spki);
|
||||
safe_strncpy(flag_tls->hostname, dns_conf_servers[i].hostname, sizeof(flag_tls->hostname));
|
||||
safe_strncpy(flag_tls->tls_host_verify, dns_conf_servers[i].tls_host_verify, sizeof(flag_tls->tls_host_verify));
|
||||
} break;
|
||||
case DNS_SERVER_TCP:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
flags.type = dns_conf_servers[i].type;
|
||||
flags.server_flag = dns_conf_servers[i].server_flag;
|
||||
flags.result_flag = dns_conf_servers[i].result_flag;
|
||||
ret = dns_client_add_server(dns_conf_servers[i].server, dns_conf_servers[i].port, dns_conf_servers[i].type, &flags);
|
||||
if (ret != 0) {
|
||||
tlog(TLOG_ERROR, "add server failed, %s:%d", dns_conf_servers[i].server, dns_conf_servers[i].port);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
hash_for_each(dns_group_table.group, i, group, node)
|
||||
{
|
||||
ret = dns_client_add_group(group->group_name);
|
||||
if (ret != 0) {
|
||||
tlog(TLOG_ERROR, "add group failed, %s", group->group_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (j = 0; j < group->server_num; j++) {
|
||||
server = group->servers[j];
|
||||
if (server == NULL) {
|
||||
continue;
|
||||
}
|
||||
ret = dns_client_add_to_group(group->group_name, server->server, server->port, server->type);
|
||||
if (ret != 0) {
|
||||
tlog(TLOG_ERROR, "add server %s to group %s failed", server->server, group->group_name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _smartdns_set_ecs_ip(void)
|
||||
{
|
||||
int ret = 0;
|
||||
if (dns_conf_ipv4_ecs.enable) {
|
||||
ret |= dns_client_set_ecs(dns_conf_ipv4_ecs.ip, dns_conf_ipv4_ecs.subnet);
|
||||
}
|
||||
|
||||
if (dns_conf_ipv6_ecs.enable) {
|
||||
ret |= dns_client_set_ecs(dns_conf_ipv6_ecs.ip, dns_conf_ipv6_ecs.subnet);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _smartdns_init_ssl(void)
|
||||
{
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_CRYPTO_thread_setup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _smartdns_destroy_ssl(void)
|
||||
{
|
||||
SSL_CRYPTO_thread_cleanup();
|
||||
ERR_free_strings();
|
||||
EVP_cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _smartdns_init(void)
|
||||
{
|
||||
int ret;
|
||||
char *logfile = SMARTDNS_LOG_FILE;
|
||||
|
||||
if (dns_conf_log_file[0] != 0) {
|
||||
logfile = dns_conf_log_file;
|
||||
}
|
||||
|
||||
ret = tlog_init(logfile, dns_conf_log_size, dns_conf_log_num, 0, 0);
|
||||
if (ret != 0) {
|
||||
tlog(TLOG_ERROR, "start tlog failed.\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
tlog_setlogscreen(verbose_screen);
|
||||
tlog_setlevel(dns_conf_log_level);
|
||||
|
||||
tlog(TLOG_NOTICE, "smartdns starting...(Copyright (C) Nick Peng <pymumu@gmail.com>, build:%s %s)", __DATE__, __TIME__);
|
||||
|
||||
if (_smartdns_init_ssl() != 0) {
|
||||
tlog(TLOG_ERROR, "init ssl failed.");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ret = _smartdns_set_ecs_ip();
|
||||
if (ret != 0) {
|
||||
tlog(TLOG_WARN, "set ecs ip address failed.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
errout:
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _smartdns_run(void)
|
||||
{
|
||||
return dns_server_run();
|
||||
}
|
||||
|
||||
static void _smartdns_exit(void)
|
||||
{
|
||||
dns_server_exit();
|
||||
dns_client_exit();
|
||||
fast_ping_exit();
|
||||
_smartdns_destroy_ssl();
|
||||
tlog_exit();
|
||||
dns_server_load_exit();
|
||||
}
|
||||
|
||||
static void _sig_exit(int signo)
|
||||
{
|
||||
dns_server_stop();
|
||||
}
|
||||
|
||||
static void _sig_error_exit(int signo, siginfo_t *siginfo, void *ct)
|
||||
{
|
||||
unsigned long PC = 0;
|
||||
ucontext_t *context = ct;
|
||||
#if defined(__i386__)
|
||||
int *pgregs = (int *)(&(context->uc_mcontext.gregs));
|
||||
PC = pgregs[REG_EIP];
|
||||
#elif defined(__x86_64__)
|
||||
int *pgregs = (int *)(&(context->uc_mcontext.gregs));
|
||||
PC = pgregs[REG_RIP];
|
||||
#elif defined(__arm__)
|
||||
PC = context->uc_mcontext.arm_pc;
|
||||
#elif defined(__aarch64__)
|
||||
PC = context->uc_mcontext.pc;
|
||||
#elif defined(__mips__)
|
||||
PC = context->uc_mcontext.pc;
|
||||
#endif
|
||||
tlog(TLOG_FATAL, "process exit with signal %d, code = %d, errno = %d, pid = %d, self = %d, pc = %#lx, addr = %#lx, build(%s %s)\n", signo, siginfo->si_code,
|
||||
siginfo->si_errno, siginfo->si_pid, getpid(), PC, (unsigned long)siginfo->si_addr, __DATE__, __TIME__);
|
||||
|
||||
sleep(1);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
static int sig_list[] = {SIGSEGV, SIGABRT, SIGBUS, SIGILL, SIGFPE};
|
||||
|
||||
static int sig_num = sizeof(sig_list) / sizeof(int);
|
||||
|
||||
static void _reg_signal(void)
|
||||
{
|
||||
struct sigaction act, old;
|
||||
int i = 0;
|
||||
act.sa_sigaction = _sig_error_exit;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
|
||||
for (i = 0; i < sig_num; i++) {
|
||||
sigaction(sig_list[i], &act, &old);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
int is_forground = 0;
|
||||
int opt;
|
||||
char config_file[MAX_LINE_LEN];
|
||||
char pid_file[MAX_LINE_LEN];
|
||||
int signal_ignore = 0;
|
||||
|
||||
safe_strncpy(config_file, SMARTDNS_CONF_FILE, MAX_LINE_LEN);
|
||||
safe_strncpy(pid_file, SMARTDNS_PID_FILE, MAX_LINE_LEN);
|
||||
|
||||
while ((opt = getopt(argc, argv, "fhc:p:Svx")) != -1) {
|
||||
switch (opt) {
|
||||
case 'f':
|
||||
is_forground = 1;
|
||||
break;
|
||||
case 'c':
|
||||
snprintf(config_file, sizeof(config_file), "%s", optarg);
|
||||
break;
|
||||
case 'p':
|
||||
snprintf(pid_file, sizeof(pid_file), "%s", optarg);
|
||||
break;
|
||||
case 'S':
|
||||
signal_ignore = 1;
|
||||
break;
|
||||
case 'x':
|
||||
verbose_screen = 1;
|
||||
break;
|
||||
case 'v':
|
||||
_show_version();
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (signal_ignore == 0) {
|
||||
_reg_signal();
|
||||
}
|
||||
|
||||
if (create_pid_file(pid_file) != 0) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (dns_server_load_conf(config_file) != 0) {
|
||||
fprintf(stderr, "load config failed.\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
ret = _smartdns_init();
|
||||
if (ret != 0) {
|
||||
usleep(100000);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
signal(SIGINT, _sig_exit);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
atexit(_smartdns_exit);
|
||||
|
||||
return _smartdns_run();
|
||||
|
||||
errout:
|
||||
|
||||
return 1;
|
||||
}
|
||||
/*************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include "art.h"
|
||||
#include "atomic.h"
|
||||
#include "dns_client.h"
|
||||
#include "dns_conf.h"
|
||||
#include "dns_server.h"
|
||||
#include "fast_ping.h"
|
||||
#include "hashtable.h"
|
||||
#include "list.h"
|
||||
#include "rbtree.h"
|
||||
#include "tlog.h"
|
||||
#include "util.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#define RESOLVE_FILE "/etc/resolv.conf"
|
||||
#define MAX_LINE_LEN 1024
|
||||
#define MAX_KEY_LEN 64
|
||||
#define SMARTDNS_PID_FILE "/var/run/smartdns.pid"
|
||||
#define TMP_BUFF_LEN_32 32
|
||||
|
||||
static int verbose_screen;
|
||||
|
||||
static 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"
|
||||
" -p [pid] pid file path\n"
|
||||
" -S ignore segment fault signal.\n"
|
||||
" -x verbose screen.\n"
|
||||
" -v dispaly version.\n"
|
||||
" -h show this help message.\n"
|
||||
|
||||
"Online help: http://pymumu.github.io/smartdns\n"
|
||||
"Copyright (C) Nick Peng <pymumu@gmail.com>\n"
|
||||
;
|
||||
/* clang-format on */
|
||||
printf("%s", help);
|
||||
}
|
||||
|
||||
static void _show_version(void)
|
||||
{
|
||||
char str_ver[256] = {0};
|
||||
#ifdef SMARTDNS_VERION
|
||||
const char *ver = SMARTDNS_VERION;
|
||||
snprintf(str_ver, sizeof(str_ver), "%s", ver);
|
||||
#else
|
||||
struct tm tm;
|
||||
get_compiled_time(&tm);
|
||||
snprintf(str_ver, sizeof(str_ver), "1.%.4d%.2d%.2d-%.2d%.2d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
|
||||
#endif
|
||||
printf("smartdns %s\n", str_ver);
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
safe_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;
|
||||
}
|
||||
|
||||
static int _smartdns_add_servers(void)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int ret = 0;
|
||||
struct dns_server_groups *group = NULL;
|
||||
struct dns_servers *server = NULL;
|
||||
struct client_dns_server_flags flags;
|
||||
|
||||
for (i = 0; i < dns_conf_server_num; i++) {
|
||||
memset(&flags, 0, sizeof(flags));
|
||||
switch (dns_conf_servers[i].type) {
|
||||
case DNS_SERVER_UDP: {
|
||||
struct client_dns_server_flag_udp *flag_udp = &flags.udp;
|
||||
flag_udp->ttl = dns_conf_servers[i].ttl;
|
||||
} break;
|
||||
case DNS_SERVER_HTTPS: {
|
||||
struct client_dns_server_flag_https *flag_http = &flags.https;
|
||||
flag_http->spi_len = dns_client_spki_decode(dns_conf_servers[i].spki, (unsigned char *)flag_http->spki);
|
||||
safe_strncpy(flag_http->hostname, dns_conf_servers[i].hostname, sizeof(flag_http->hostname));
|
||||
safe_strncpy(flag_http->path, dns_conf_servers[i].path, sizeof(flag_http->path));
|
||||
safe_strncpy(flag_http->httphost, dns_conf_servers[i].httphost, sizeof(flag_http->httphost));
|
||||
safe_strncpy(flag_http->tls_host_verify, dns_conf_servers[i].tls_host_verify, sizeof(flag_http->tls_host_verify));
|
||||
} break;
|
||||
case DNS_SERVER_TLS: {
|
||||
struct client_dns_server_flag_tls *flag_tls = &flags.tls;
|
||||
flag_tls->spi_len = dns_client_spki_decode(dns_conf_servers[i].spki, (unsigned char *)flag_tls->spki);
|
||||
safe_strncpy(flag_tls->hostname, dns_conf_servers[i].hostname, sizeof(flag_tls->hostname));
|
||||
safe_strncpy(flag_tls->tls_host_verify, dns_conf_servers[i].tls_host_verify, sizeof(flag_tls->tls_host_verify));
|
||||
} break;
|
||||
case DNS_SERVER_TCP:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
flags.type = dns_conf_servers[i].type;
|
||||
flags.server_flag = dns_conf_servers[i].server_flag;
|
||||
flags.result_flag = dns_conf_servers[i].result_flag;
|
||||
ret = dns_client_add_server(dns_conf_servers[i].server, dns_conf_servers[i].port, dns_conf_servers[i].type, &flags);
|
||||
if (ret != 0) {
|
||||
tlog(TLOG_ERROR, "add server failed, %s:%d", dns_conf_servers[i].server, dns_conf_servers[i].port);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
hash_for_each(dns_group_table.group, i, group, node)
|
||||
{
|
||||
ret = dns_client_add_group(group->group_name);
|
||||
if (ret != 0) {
|
||||
tlog(TLOG_ERROR, "add group failed, %s", group->group_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (j = 0; j < group->server_num; j++) {
|
||||
server = group->servers[j];
|
||||
if (server == NULL) {
|
||||
continue;
|
||||
}
|
||||
ret = dns_client_add_to_group(group->group_name, server->server, server->port, server->type);
|
||||
if (ret != 0) {
|
||||
tlog(TLOG_ERROR, "add server %s to group %s failed", server->server, group->group_name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _smartdns_set_ecs_ip(void)
|
||||
{
|
||||
int ret = 0;
|
||||
if (dns_conf_ipv4_ecs.enable) {
|
||||
ret |= dns_client_set_ecs(dns_conf_ipv4_ecs.ip, dns_conf_ipv4_ecs.subnet);
|
||||
}
|
||||
|
||||
if (dns_conf_ipv6_ecs.enable) {
|
||||
ret |= dns_client_set_ecs(dns_conf_ipv6_ecs.ip, dns_conf_ipv6_ecs.subnet);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _smartdns_init_ssl(void)
|
||||
{
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_CRYPTO_thread_setup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _smartdns_destroy_ssl(void)
|
||||
{
|
||||
SSL_CRYPTO_thread_cleanup();
|
||||
ERR_free_strings();
|
||||
EVP_cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _smartdns_init(void)
|
||||
{
|
||||
int ret;
|
||||
char *logfile = SMARTDNS_LOG_FILE;
|
||||
|
||||
if (dns_conf_log_file[0] != 0) {
|
||||
logfile = dns_conf_log_file;
|
||||
}
|
||||
|
||||
ret = tlog_init(logfile, dns_conf_log_size, dns_conf_log_num, 0, 0);
|
||||
if (ret != 0) {
|
||||
tlog(TLOG_ERROR, "start tlog failed.\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
tlog_setlogscreen(verbose_screen);
|
||||
tlog_setlevel(dns_conf_log_level);
|
||||
|
||||
tlog(TLOG_NOTICE, "smartdns starting...(Copyright (C) Nick Peng <pymumu@gmail.com>, build:%s %s)", __DATE__, __TIME__);
|
||||
|
||||
if (_smartdns_init_ssl() != 0) {
|
||||
tlog(TLOG_ERROR, "init ssl failed.");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ret = _smartdns_set_ecs_ip();
|
||||
if (ret != 0) {
|
||||
tlog(TLOG_WARN, "set ecs ip address failed.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
errout:
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _smartdns_run(void)
|
||||
{
|
||||
return dns_server_run();
|
||||
}
|
||||
|
||||
static void _smartdns_exit(void)
|
||||
{
|
||||
dns_server_exit();
|
||||
dns_client_exit();
|
||||
fast_ping_exit();
|
||||
_smartdns_destroy_ssl();
|
||||
tlog_exit();
|
||||
dns_server_load_exit();
|
||||
}
|
||||
|
||||
static void _sig_exit(int signo)
|
||||
{
|
||||
dns_server_stop();
|
||||
}
|
||||
|
||||
static void _sig_error_exit(int signo, siginfo_t *siginfo, void *ct)
|
||||
{
|
||||
unsigned long PC = 0;
|
||||
ucontext_t *context = ct;
|
||||
#if defined(__i386__)
|
||||
int *pgregs = (int *)(&(context->uc_mcontext.gregs));
|
||||
PC = pgregs[REG_EIP];
|
||||
#elif defined(__x86_64__)
|
||||
int *pgregs = (int *)(&(context->uc_mcontext.gregs));
|
||||
PC = pgregs[REG_RIP];
|
||||
#elif defined(__arm__)
|
||||
PC = context->uc_mcontext.arm_pc;
|
||||
#elif defined(__aarch64__)
|
||||
PC = context->uc_mcontext.pc;
|
||||
#elif defined(__mips__)
|
||||
PC = context->uc_mcontext.pc;
|
||||
#endif
|
||||
tlog(TLOG_FATAL, "process exit with signal %d, code = %d, errno = %d, pid = %d, self = %d, pc = %#lx, addr = %#lx, build(%s %s)\n", signo, siginfo->si_code,
|
||||
siginfo->si_errno, siginfo->si_pid, getpid(), PC, (unsigned long)siginfo->si_addr, __DATE__, __TIME__);
|
||||
|
||||
sleep(1);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
static int sig_list[] = {SIGSEGV, SIGABRT, SIGBUS, SIGILL, SIGFPE};
|
||||
|
||||
static int sig_num = sizeof(sig_list) / sizeof(int);
|
||||
|
||||
static void _reg_signal(void)
|
||||
{
|
||||
struct sigaction act, old;
|
||||
int i = 0;
|
||||
act.sa_sigaction = _sig_error_exit;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
|
||||
for (i = 0; i < sig_num; i++) {
|
||||
sigaction(sig_list[i], &act, &old);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
int is_forground = 0;
|
||||
int opt;
|
||||
char config_file[MAX_LINE_LEN];
|
||||
char pid_file[MAX_LINE_LEN];
|
||||
int signal_ignore = 0;
|
||||
|
||||
safe_strncpy(config_file, SMARTDNS_CONF_FILE, MAX_LINE_LEN);
|
||||
safe_strncpy(pid_file, SMARTDNS_PID_FILE, MAX_LINE_LEN);
|
||||
|
||||
while ((opt = getopt(argc, argv, "fhc:p:Svx")) != -1) {
|
||||
switch (opt) {
|
||||
case 'f':
|
||||
is_forground = 1;
|
||||
break;
|
||||
case 'c':
|
||||
snprintf(config_file, sizeof(config_file), "%s", optarg);
|
||||
break;
|
||||
case 'p':
|
||||
snprintf(pid_file, sizeof(pid_file), "%s", optarg);
|
||||
break;
|
||||
case 'S':
|
||||
signal_ignore = 1;
|
||||
break;
|
||||
case 'x':
|
||||
verbose_screen = 1;
|
||||
break;
|
||||
case 'v':
|
||||
_show_version();
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (signal_ignore == 0) {
|
||||
_reg_signal();
|
||||
}
|
||||
|
||||
if (create_pid_file(pid_file) != 0) {
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (dns_server_load_conf(config_file) != 0) {
|
||||
fprintf(stderr, "load config failed.\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
ret = _smartdns_init();
|
||||
if (ret != 0) {
|
||||
usleep(100000);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
signal(SIGINT, _sig_exit);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
atexit(_smartdns_exit);
|
||||
|
||||
return _smartdns_run();
|
||||
|
||||
errout:
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user