diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index b332d55..f5befe1 100644 --- a/etc/smartdns/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -4,6 +4,12 @@ # server-name smartdns # +# dns server run ser +# user [username] +# example: run as nobody +# user nobody +# + # Include another configuration options # conf-file [file] # conf-file blacklist-ip.conf @@ -90,6 +96,7 @@ cache-size 16384 # Enable IPV4, IPV6 dual stack IP optimization selection strategy # dualstack-ip-selection-threshold [num] (0~1000) +# dualstack-ip-allow-force-AAAA [yes|no] # dualstack-ip-selection [yes|no] # dualstack-ip-selection yes diff --git a/src/dns_conf.c b/src/dns_conf.c index 4fa075d..767ea11 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -120,6 +120,8 @@ int dns_conf_force_AAAA_SOA; int dns_conf_force_no_cname; int dns_conf_ipset_timeout_enable; +char dns_conf_user[DNS_CONF_USRNAME_LEN]; + /* ECS */ struct dns_edns_client_subnet dns_conf_ipv4_ecs; struct dns_edns_client_subnet dns_conf_ipv6_ecs; @@ -1908,6 +1910,7 @@ static struct config_item _config_item[] = { CONF_CUSTOM("hosts-file", _conf_hosts_file, NULL), CONF_STRING("ca-file", (char *)&dns_conf_ca_file, DNS_MAX_PATH), CONF_STRING("ca-path", (char *)&dns_conf_ca_path, DNS_MAX_PATH), + CONF_STRING("user", (char *)&dns_conf_user, sizeof(dns_conf_user)), CONF_CUSTOM("conf-file", config_addtional_file, NULL), CONF_END(), }; @@ -2025,13 +2028,33 @@ static int _dns_conf_speed_check_mode_verify(void) return 0; } +static int _dns_ping_cap_check(void) +{ + int has_ping = 0; + int has_raw_cap; + + has_raw_cap = has_network_raw_cap(); + has_ping = has_unprivileged_ping(); + if (has_ping == 0) { + if (errno == EACCES && has_raw_cap == 0) { + tlog(TLOG_WARN, "unpriviledged ping is disabled, please enable by setting net.ipv4.ping_group_range"); + } + } + + if (has_ping == 1 || has_raw_cap == 1) { + dns_has_cap_ping = 1; + } + + return 0; +} + static int _dns_conf_load_pre(void) { if (_dns_server_load_conf_init() != 0) { goto errout; } - dns_has_cap_ping = has_network_raw_cap(); + _dns_ping_cap_check(); return 0; diff --git a/src/dns_conf.h b/src/dns_conf.h index 38d0dc3..58452fd 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -40,6 +40,7 @@ extern "C" { #define DNS_GROUP_NAME_LEN 32 #define DNS_NAX_GROUP_NUMBER 16 #define DNS_MAX_IPLEN 64 +#define DNS_CONF_USRNAME_LEN 32 #define DNS_MAX_SPKI_LEN 64 #define DNS_MAX_URL_LEN 256 #define DNS_MAX_PATH 1024 @@ -307,6 +308,8 @@ extern int dns_conf_ipset_timeout_enable; extern int dns_conf_force_no_cname; +extern char dns_conf_user[DNS_CONF_USRNAME_LEN]; + extern struct dns_edns_client_subnet dns_conf_ipv4_ecs; extern struct dns_edns_client_subnet dns_conf_ipv6_ecs; diff --git a/src/fast_ping.c b/src/fast_ping.c index c7dd48e..61b0a0a 100644 --- a/src/fast_ping.c +++ b/src/fast_ping.c @@ -130,6 +130,7 @@ struct fast_ping_struct { unsigned short ident; int epoll_fd; + int no_unprivileged_ping; int fd_icmp; struct ping_host_struct icmp_host; int fd_icmp6; @@ -664,21 +665,34 @@ static int _fast_ping_create_icmp_sock(FAST_PING_TYPE type) switch (type) { case FAST_PING_ICMP: - fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (ping.no_unprivileged_ping == 0) { + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + } else { + fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (fd > 0) { + _fast_ping_install_filter_v4(fd); + } + } if (fd < 0) { tlog(TLOG_ERROR, "create icmp socket failed, %s\n", strerror(errno)); goto errout; } - _fast_ping_install_filter_v4(fd); icmp_host = &ping.icmp_host; break; case FAST_PING_ICMP6: - fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + if (ping.no_unprivileged_ping == 0) { + fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6); + } else { + fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + if (fd > 0) { + _fast_ping_install_filter_v6(fd); + } + } + if (fd < 0) { tlog(TLOG_ERROR, "create icmp socket failed, %s\n", strerror(errno)); goto errout; } - _fast_ping_install_filter_v6(fd); setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)); setsockopt(fd, IPPROTO_IPV6, IPV6_2292HOPLIMIT, &on, sizeof(on)); setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)); @@ -1172,9 +1186,11 @@ static struct fast_ping_packet *_fast_ping_icmp6_packet(struct ping_host_struct return NULL; } - if (icmp6->icmp6_id != ping.ident) { - tlog(TLOG_ERROR, "ident failed, %d:%d", icmp6->icmp6_id, ping.ident); - return NULL; + if (ping.no_unprivileged_ping) { + if (icmp6->icmp6_id != ping.ident) { + tlog(TLOG_ERROR, "ident failed, %d:%d", icmp6->icmp6_id, ping.ident); + return NULL; + } } return packet; @@ -1189,11 +1205,6 @@ static struct fast_ping_packet *_fast_ping_icmp_packet(struct ping_host_struct * int hlen; int icmp_len; - if (ip->ip_p != IPPROTO_ICMP) { - tlog(TLOG_ERROR, "ip type faild, %d:%d", ip->ip_p, IPPROTO_ICMP); - return NULL; - } - hlen = ip->ip_hl << 2; packet = (struct fast_ping_packet *)(packet_data + hlen); icmp = &packet->icmp; @@ -1205,15 +1216,23 @@ static struct fast_ping_packet *_fast_ping_icmp_packet(struct ping_host_struct * return NULL; } + if (ping.no_unprivileged_ping) { + if (ip->ip_p != IPPROTO_ICMP) { + tlog(TLOG_ERROR, "ip type faild, %d:%d", ip->ip_p, IPPROTO_ICMP); + return NULL; + } + + if (icmp->icmp_id != ping.ident) { + tlog(TLOG_ERROR, "ident failed, %d:%d", icmp->icmp_id, ping.ident); + return NULL; + } + } + if (icmp->icmp_type != ICMP_ECHOREPLY) { tlog(TLOG_DEBUG, "icmp type faild, %d:%d", icmp->icmp_type, ICMP_ECHOREPLY); return NULL; } - if (icmp->icmp_id != ping.ident) { - tlog(TLOG_ERROR, "ident failed, %d:%d", icmp->icmp_id, ping.ident); - return NULL; - } return packet; } @@ -1659,6 +1678,7 @@ int fast_ping_init(void) pthread_mutex_init(&ping.lock, NULL); hash_init(ping.addrmap); ping.epoll_fd = epollfd; + ping.no_unprivileged_ping = !has_unprivileged_ping(); ping.ident = (getpid() & 0XFFFF); ping.run = 1; ret = pthread_create(&ping.tid, &attr, _fast_ping_work, NULL); diff --git a/src/smartdns.c b/src/smartdns.c index 416c5be..f9eda8a 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -31,12 +31,15 @@ #include #include #include +#include #include #include +#include #include #include #include #include +#include #include #include #include @@ -49,6 +52,77 @@ static int verbose_screen; +int capget(struct __user_cap_header_struct *header, struct __user_cap_data_struct *cap); +int capset(struct __user_cap_header_struct *header, struct __user_cap_data_struct *cap); + +int get_uid_gid(int *uid, int *gid) +{ + struct passwd *result = NULL; + struct passwd pwd; + char *buf = NULL; + size_t bufsize; + int ret = -1; + + if (dns_conf_user[0] == '\0') { + return -1; + } + + bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize == -1) { + bufsize = 1024 * 16; + } + + buf = malloc(bufsize); + if (buf == NULL) { + goto out; + } + + ret = getpwnam_r(dns_conf_user, &pwd, buf, bufsize, &result); + if (ret != 0) { + return -1; + } + + *uid = result->pw_uid; + *gid = result->pw_gid; + +out: + if (buf) { + free(buf); + } + + return ret; +} + +int drop_root_privilege(void) +{ + struct __user_cap_data_struct cap; + struct __user_cap_header_struct header; + header.version = _LINUX_CAPABILITY_VERSION; + header.pid = 0; + int uid; + int gid; + + if (get_uid_gid(&uid, &gid) != 0) { + return -1; + } + + if (capget(&header, &cap) < 0) { + return -1; + } + + prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + cap.effective |= (1 << CAP_NET_RAW | 1 << CAP_NET_ADMIN); + cap.permitted |= (1 << CAP_NET_RAW | 1 << CAP_NET_ADMIN); + setuid(uid); + setgid(gid); + if (capset(&header, &cap) < 0) { + return -1; + } + + prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0); + return 0; +} + static void _help(void) { /* clang-format off */ @@ -322,6 +396,7 @@ static int _smartdns_run(void) static void _smartdns_exit(void) { + tlog(TLOG_INFO, "smartdns starting exit..."); dns_server_exit(); dns_client_exit(); fast_ping_exit(); @@ -332,6 +407,7 @@ static void _smartdns_exit(void) static void _sig_exit(int signo) { + tlog(TLOG_INFO, "start stop smartdns"); dns_server_stop(); } @@ -445,6 +521,8 @@ int main(int argc, char *argv[]) goto errout; } + drop_root_privilege(); + ret = _smartdns_init(); if (ret != 0) { usleep(100000); diff --git a/src/util.c b/src/util.c index 1fb0730..1586c42 100644 --- a/src/util.c +++ b/src/util.c @@ -1010,6 +1010,25 @@ int has_network_raw_cap(void) return 1; } +int has_unprivileged_ping(void) +{ + int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + if (fd < 0) { + return 0; + } + + close(fd); + + fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6); + if (fd < 0) { + return 0; + } + + close(fd); + + return 1; +} + int set_sock_keepalive(int fd, int keepidle, int keepinterval, int keepcnt) { const int yes = 1; diff --git a/src/util.h b/src/util.h index 98abbb6..b4411af 100644 --- a/src/util.h +++ b/src/util.h @@ -104,6 +104,8 @@ int is_numeric(const char *str); int has_network_raw_cap(void); +int has_unprivileged_ping(void); + int set_sock_keepalive(int fd, int keepidle, int keepinterval, int keepcnt); int set_sock_lingertime(int fd, int time);