Commit 73502fdc authored by Chen Wei's avatar Chen Wei

--server is working

parent 352d236d
### DNSMASQ fork for improving --ipsets and --server performance ## DNSMASQ fork for improving --ipsets and --server performance
## Current status ### Current status
The ipsets lookup has been rewritten. It scales pretty well even with thousands The --ipsets and --server lookup has been rewritten. It scales well with thousands
of --ipsets entries. of --ipsets and --server entries.
## In progress
Still working on --server. ### In progress
Still need test and tweak.
[Contact me](mailto: weichen302@gmail.com) [Contact me](mailto: weichen302@gmail.com)
...@@ -423,15 +423,15 @@ void free_dicttree (struct dict_node *node) ...@@ -423,15 +423,15 @@ void free_dicttree (struct dict_node *node)
static inline int is_same_server(struct server *s1, struct server *s2) static inline int is_same_server(struct server *s1, struct server *s2)
{ {
if (memcmp(&s1->addr, &s2->addr, sizeof(union mysockaddr)) != 0) if (memcmp(&s1->addr, &s2->addr, sizeof(union mysockaddr)) != 0)
return -1; return 0;
if (strncmp(s1->interface, s2->interface, IF_NAMESIZE + 1) != 0) if (strncmp(s1->interface, s2->interface, IF_NAMESIZE + 1) != 0)
return -1; return 0;
if (s1->flags != s2->flags) if (s1->flags != s2->flags)
return -1; return 0;
return 0; return 1;
} }
/* duplicate a struct server, but only copy addr, source_addr, interfaces, and /* duplicate a struct server, but only copy addr, source_addr, interfaces, and
......
...@@ -254,9 +254,6 @@ int main (int argc, char **argv) ...@@ -254,9 +254,6 @@ int main (int argc, char **argv)
ipset_init(); ipset_init();
#endif #endif
if (daemon->dh_special_domains == NULL)
daemon->dh_special_domains = new_dictnode(NULL, 0);
#if defined(HAVE_LINUX_NETWORK) #if defined(HAVE_LINUX_NETWORK)
netlink_init(); netlink_init();
#elif defined(HAVE_BSD_NETWORK) #elif defined(HAVE_BSD_NETWORK)
......
...@@ -118,91 +118,111 @@ int send_from(int fd, int nowild, char *packet, size_t len, ...@@ -118,91 +118,111 @@ int send_from(int fd, int nowild, char *packet, size_t len,
return 1; return 1;
} }
static unsigned int search_servers(time_t now, struct all_addr **addrpp, /* search domain pattern for --address, --local, --server, --rebind-domain-ok
unsigned int qtype, char *qdomain, int *type,
char **domain, int *norebind, if matches --address, the address is stored in addrpp
struct server **fwdserv)
if matches --server, a pointer to one of servers in daemon->servers is
stored in fwdserv, unless --server=/example.org/#, in which case fwdserv
will be NULL, means use normal server
if matches --rebind-domain-ok, the pass in norebind will be set to 1
we find largest match, e.g. given pattern debian.org and cn.debian.org,
ftp.cn.debian.org will match cn.debian.org
*/
static unsigned int
search_servers (time_t now, struct all_addr **addrpp,
unsigned int qtype, char *qdomain, int *type,
char **domain, int *norebind, struct server **fwdserv)
{ {
/* If the query ends in the domain in one of our servers, set unsigned int namelen = strlen (qdomain);
domain to point to that name. We find the largest match to allow both unsigned int flags = 0;
domain.org and sub.domain.org to exist. */ unsigned int sflag;
struct dict_node *np;
unsigned int namelen = strlen (qdomain); struct special_domain *obj;
struct server *serv;
unsigned int flags = 0; *type = 0;
unsigned int sflag; np = match_domain (daemon->dh_special_domains, qdomain);
struct dict_node *np; if (np != NULL)
struct special_domain *obj; {
obj = (struct special_domain *) np->obj;
np = match_domain (daemon->dh_special_domains, qdomain);
if (np != NULL)
{
obj = (struct special_domain *) np->obj;
serv = obj->server;
*type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV |
SERV_NO_REBIND);
if (obj->domain_flags & SERV_NO_REBIND) *type |= SERV_HAS_DOMAIN;
*norebind = 1;
// no server, domain is local only if (obj->domain_flags & SERV_NO_REBIND)
if (obj->domain_flags & SERV_NO_ADDR) { *norebind = 1;
// no server, domain is local only
if (obj->domain_flags & SERV_NO_ADDR)
{
flags = F_NXDOMAIN; flags = F_NXDOMAIN;
} else if (obj->domain_flags == SERV_LITERAL_ADDRESS) {
// --address and AF matches }
sflag = obj->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; else if (obj->domain_flags & SERV_LITERAL_ADDRESS)
if (sflag & qtype) {
{ // --address and AF matches
flags = sflag; sflag = obj->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
if (serv->addr.sa.sa_family == AF_INET) if (sflag & qtype)
*addrpp = (struct all_addr *) &serv->addr.in.sin_addr; {
flags = sflag;
if (obj->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *) &obj->addr.in.sin_addr;
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else else
*addrpp = (struct all_addr *) &serv->addr.in6.sin6_addr; *addrpp = (struct all_addr *) &obj->addr.in6.sin6_addr;
#endif #endif
} }
} else {
*fwdserv = obj->server;
flags = 0;
} }
} else if (obj->domain_flags & SERV_USE_RESOLV)
else {
{ // --server=8.8.8.8
*type = 0; // use normal server *type = 0; // use normal server
*fwdserv = NULL; *fwdserv = NULL;
}
}
else
{
*fwdserv = obj->server;
flags = 0;
}
}
else
{
*type = 0; /* use normal servers for this domain */
*fwdserv = NULL;
}
/* have go through all servers in chain, now let's see what flag survive */ if (flags == 0 && !(qtype & F_QUERY) &&
if (flags == 0 && !(qtype & F_QUERY) && option_bool (OPT_NODOTS_LOCAL) && !strchr (qdomain, '.')
option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0) && namelen != 0)
/* don't forward A or AAAA queries for simple names, except the empty name */ /* don't forward A or AAAA queries for simple names, except the empty name */
flags = F_NOERR; flags = F_NOERR;
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now)) if (flags == F_NXDOMAIN && check_for_local_domain (qdomain, now))
flags = F_NOERR; flags = F_NOERR;
if (flags) if (flags)
{ {
int logflags = 0; int logflags = 0;
if (flags == F_NXDOMAIN || flags == F_NOERR) if (flags == F_NXDOMAIN || flags == F_NOERR)
logflags = F_NEG | qtype; logflags = F_NEG | qtype;
log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, NULL); log_query (logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp,
NULL);
} }
else if ((*type) & SERV_USE_RESOLV) else if ((*type) & SERV_USE_RESOLV)
{ {
*type = 0; /* use normal servers for this domain */ *type = 0; /* use normal servers for this domain */
*domain = NULL; *domain = NULL;
} }
return flags;
return flags;
} }
//forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0, 0);
static int forward_query(int udpfd, union mysockaddr *udpaddr, static int forward_query(int udpfd, union mysockaddr *udpaddr,
struct all_addr *dst_addr, unsigned int dst_iface, struct all_addr *dst_addr, unsigned int dst_iface,
struct dns_header *header, size_t plen, time_t now, struct dns_header *header, size_t plen, time_t now,
...@@ -388,16 +408,29 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, ...@@ -388,16 +408,29 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
// we have the server for our domain by lookup daemon->dh_special_domains // we have the server for our domain by lookup daemon->dh_special_domains
int fd; int fd;
/* find server socket to use, may need to get random one. */ /* didn't find a server matches query domain */
if (fwdserv == NULL)
{
for (fwdserv = daemon->servers;
fwdserv != NULL; fwdserv = fwdserv->next)
{
//TODO figure out how to skip unresponsive server
if (!(fwdserv->flags & (SERV_HAS_DOMAIN | SERV_LOOP)))
{
break;
}
}
}
if (fwdserv->sfd) if (fwdserv->sfd)
fd = fwdserv->sfd->fd; fd = fwdserv->sfd->fd;
else else
{ {
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
if (fwdserv->addr.sa.sa_family == AF_INET6) if (fwdserv->addr.sa.sa_family == AF_INET6)
{ {
if (forward->rfd6 == NULL) if (forward->rfd6 == NULL)
forward->rfd6 = allocate_rfd(AF_INET6); forward->rfd6 = allocate_rfd (AF_INET6);
daemon->rfd_save = forward->rfd6; daemon->rfd_save = forward->rfd6;
fd = forward->rfd6->fd; fd = forward->rfd6->fd;
} }
...@@ -405,30 +438,28 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, ...@@ -405,30 +438,28 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#endif #endif
{ {
if (forward->rfd4 == NULL) if (forward->rfd4 == NULL)
forward->rfd4 = allocate_rfd(AF_INET); forward->rfd4 = allocate_rfd (AF_INET);
daemon->rfd_save = forward->rfd4; daemon->rfd_save = forward->rfd4;
fd = forward->rfd4->fd; fd = forward->rfd4->fd;
} }
#ifdef HAVE_CONNTRACK #ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */ /* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK)) if (option_bool (OPT_CONNTRACK))
{ {
unsigned int mark; unsigned int mark;
if (get_incoming_mark(&forward->source, &forward->dest, 0, &mark)) if (get_incoming_mark (&forward->source, &forward->dest, 0, &mark))
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); setsockopt (fd, SOL_SOCKET, SO_MARK, &mark,
sizeof (unsigned int));
} }
#endif #endif
} }
//TODO retry
if (sendto(fd, (char *)header, plen, 0, if (sendto (fd, (char *) header, plen, 0,
&fwdserv->addr.sa, &fwdserv->addr.sa, sa_len (&fwdserv->addr)) == -1)
sa_len(&fwdserv->addr)) == -1)
{ {
retry_send(); retry_send ();
} }
else else
...@@ -436,27 +467,27 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, ...@@ -436,27 +467,27 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
/* Keep info in case we want to re-send this packet */ /* Keep info in case we want to re-send this packet */
daemon->srv_save = fwdserv; daemon->srv_save = fwdserv;
daemon->packet_len = plen; daemon->packet_len = plen;
if (!gotname) if (!gotname)
strcpy(daemon->namebuff, "query"); strcpy (daemon->namebuff, "query");
if (fwdserv->addr.sa.sa_family == AF_INET) if (fwdserv->addr.sa.sa_family == AF_INET)
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, log_query (F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&fwdserv->addr.in.sin_addr, NULL); (struct all_addr *) &fwdserv->addr.in.sin_addr, NULL);
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else else
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, log_query (F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&fwdserv->addr.in6.sin6_addr, NULL); (struct all_addr *) &fwdserv->addr.in6.sin6_addr, NULL);
#endif #endif
fwdserv->queries++; fwdserv->queries++;
forwarded = 1; forwarded = 1;
forward->sentto = fwdserv; forward->sentto = fwdserv;
if (forward->forwardall) if (forward->forwardall)
forward->forwardall++; forward->forwardall++;
} }
if (forwarded) if (forwarded)
return 1; return 1;
/* could not send on, prepare to return */ /* could not send on, prepare to return */
header->id = htons(forward->orig_id); header->id = htons(forward->orig_id);
free_frec(forward); /* cancel */ free_frec(forward); /* cancel */
......
...@@ -1466,8 +1466,10 @@ void check_servers(void) ...@@ -1466,8 +1466,10 @@ void check_servers(void)
char *s1, *s2; char *s1, *s2;
if (!(serv->flags & SERV_HAS_DOMAIN)) if (!(serv->flags & SERV_HAS_DOMAIN))
s1 = _("unqualified"), s2 = _("names"); s1 = _("unqualified"), s2 = _("names");
/*
else if (strlen(serv->domain) == 0) else if (strlen(serv->domain) == 0)
s1 = _("default"), s2 = ""; s1 = _("default"), s2 = "";
*/
else else
s1 = _("domain"), s2 = serv->domain; s1 = _("domain"), s2 = serv->domain;
......
...@@ -1966,7 +1966,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma ...@@ -1966,7 +1966,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
serv = opt_malloc(sizeof(struct server)); serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server)); memset(serv, 0, sizeof(struct server));
serv->domain = d; serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR; serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers; serv->next = daemon->servers;
daemon->servers = serv; daemon->servers = serv;
} }
...@@ -2203,139 +2203,185 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma ...@@ -2203,139 +2203,185 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
arg = comma; arg = comma;
} while (arg); } while (arg);
break; break;
case 'S': /* --server */ case 'S': /* --server */
case LOPT_LOCAL: /* --local */ case LOPT_LOCAL: /* --local */
case 'A': /* --address */ case 'A': /* --address */
case LOPT_NO_REBIND: /* --rebind-domain-ok */ case LOPT_NO_REBIND: /* --rebind-domain-ok */
//TODO fast hash lookup
{ {
unhide_metas(arg); unhide_metas (arg);
char *start_addr; char *start_addr, *s;
char *err; char *err;
struct server newlist; struct server newserv;
struct dict_node *np; struct dict_node *np;
struct special_domain *obj; struct special_domain *obj;
memset(&newlist, 0, sizeof(struct server)); memset (&newserv, 0, sizeof (struct server));
#ifdef HAVE_LOOP #ifdef HAVE_LOOP
newlist.uid = rand32(); newserv.uid = rand32 ();
#endif #endif
if (arg == NULL) if (arg == NULL)
break; break;
if (daemon->dh_special_domains == NULL)
daemon->dh_special_domains = new_dictnode (NULL, 0);
// scan the address part first // scan the address part first
// --xxxx=/example.org/ample.com/temple.net/address-of-server // --xxxx=/example.org/ample.com/temple.net/address-of-server
// ^ // ^
start_addr = NULL; start_addr = NULL;
if (strchr(arg, '/') == NULL) { if (strchr (arg, '/') == NULL)
{
// --xxxx=example.org (only availabe for --rebind-domain-ok) // --xxxx=example.org (only availabe for --rebind-domain-ok)
if (option == LOPT_NO_REBIND) if (option == LOPT_NO_REBIND)
newlist.flags |= SERV_NO_REBIND; newserv.flags |= SERV_NO_REBIND;
else if (option == 'S') else if (option == 'S')
{
// --server=8.8.8.8 // --server=8.8.8.8
start_addr = arg; start_addr = arg;
}
} else { }
for (start_addr = arg; else
(start_addr = strchr(start_addr, '/')) != NULL; ) ; {
for (s = arg; *s != '\0'; s++)
{
if (*s == '/')
start_addr = s;
}
start_addr++; start_addr++;
} }
/* --xxxx=/example.org/# , here "#" means use standard server*/ /* --xxxx=/example.org/# , here "#" means use standard server */
if (start_addr != NULL) { if (start_addr != NULL)
if (*start_addr == '#') { {
newlist.flags |= SERV_USE_RESOLV; if (*start_addr == '#')
{
newserv.flags |= SERV_USE_RESOLV;
}
else if (*start_addr == '\0')
/* --xxxx=/example.org/here-is-empty */ /* --xxxx=/example.org/here-is-empty */
} else if (*start_addr == '\0') { {
if (!(newlist.flags & SERV_NO_REBIND)) if (!(newserv.flags & SERV_NO_REBIND))
newlist.flags |= SERV_NO_ADDR; /* no server */ newserv.flags |= SERV_NO_ADDR; /* no server */
} else { if (option == 'S')
{
ret_err ("--server must specify server address");
}
if (option == 'A')
{
ret_err ("--address must specify address");
}
}
else
{
/* --xxxx=/example.org/8.8.8.8#53@source-ip|interface#port */ /* --xxxx=/example.org/8.8.8.8#53@source-ip|interface#port */
err = parse_server(arg, &newlist.addr, &newlist.source_addr, newlist.interface, &newlist.flags); err =
parse_server (start_addr, &newserv.addr, &newserv.source_addr,
newserv.interface, &newserv.flags);
if (err) if (err)
ret_err(err); ret_err (err);
} }
} }
// --server // --server
if (servers_only && option == 'S') if (servers_only && option == 'S')
newlist.flags |= SERV_FROM_FILE; newserv.flags |= SERV_FROM_FILE;
// --rebind-domain-ok // --rebind-domain-ok
if (option == LOPT_NO_REBIND) if (option == LOPT_NO_REBIND)
newlist.flags |= SERV_NO_REBIND; newserv.flags |= SERV_NO_REBIND;
// --address will be handled inside the domain dict_node // --address will be handled inside the domain dict_node
// the arg pattern can be // the arg pattern can be
// --xxxx=example.org (only availabe for --rebind-domain-ok) or // --xxxx=example.org (only availabe for --rebind-domain-ok) or
// --xxxx=/example.org/ or // --xxxx=/example.org/ or
// --xxxx=/example.org/ample.com/temple.net/ // --xxxx=/example.org/ample.com/temple.net/
if (*arg == '/' || option == LOPT_NO_REBIND) if (*arg == '/' || option == LOPT_NO_REBIND)
{ {
int rebind = !(*arg == '/'); int rebind = !(*arg == '/');
char *end = NULL; char *end = NULL;
if (!rebind) if (!rebind)
arg++; arg++;
while (rebind || (end = split_chr(arg, '/'))) while (rebind || (end = split_chr (arg, '/')))
{ {
char *domain = NULL; char *domain = NULL;
/* elide leading dots - they are implied in the search algorithm */ /* elide leading dots - they are implied in the search algorithm */
while (*arg == '.') arg++; while (*arg == '.')
/* # matches everything and becomes a zero length domain string */ arg++;
if (strcmp(arg, "#") == 0)
domain = "";
else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
option = '?';
//TODO --address=/#/1.2.3.4
if (strcmp (arg, "#") == 0)
domain = "";
else if (strlen (arg) != 0 && !(domain = canonicalise_opt (arg)))
option = '?';
np = add_or_lookup_domain(daemon->dh_special_domains, domain); np = add_or_lookup_domain (daemon->dh_special_domains, domain);
if (np->obj == NULL) { if (np->obj == NULL)
obj = opt_malloc(sizeof(struct special_domain)); {
memset(obj, 0, sizeof(struct special_domain)); obj = opt_malloc (sizeof (struct special_domain));
memset (obj, 0, sizeof (struct special_domain));
obj->domain_flags = 0; obj->domain_flags = 0;
} else { }
else
{
obj = (struct special_domain *) np->obj; obj = (struct special_domain *) np->obj;
} }
if (option == 'A') { obj->domain_flags = newserv.flags;
if (option == 'A')
{
obj->server = NULL; obj->server = NULL;
obj->domain_flags = SERV_LITERAL_ADDRESS; obj->domain_flags |= SERV_LITERAL_ADDRESS;
memcpy(&obj->addr, &newlist.addr, sizeof(union mysockaddr)); memcpy (&obj->addr, &newserv.addr, sizeof (union mysockaddr));
} else if (option == 'S') { }
else if (option == 'S')
{
// pointer to one of servers in daemon->servers link list, // pointer to one of servers in daemon->servers link list,
// no memory is leaked if obj->server been overwritten // no memory is leaked if obj->server been overwritten
obj->server = lookup_or_install_new_server(&newlist); obj->server = lookup_or_install_new_server (&newserv);
} obj->server->flags |= SERV_HAS_DOMAIN;
obj->server->domain = NULL;
}
if (option == LOPT_NO_REBIND) { if (option == LOPT_NO_REBIND)
{
// the rebind flag here instead of the one in struct server // the rebind flag here instead of the one in struct server
// will be used by forward // will be used by forward
obj->domain_flags |= SERV_NO_REBIND; obj->domain_flags |= SERV_NO_REBIND;
} }
if (option == LOPT_LOCAL) { if (option == LOPT_LOCAL)
{
obj->domain_flags |= SERV_NO_ADDR; obj->domain_flags |= SERV_NO_ADDR;
} }
//newlist.flags |= domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS; //newserv.flags |= domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
np->obj = (void *) obj; np->obj = (void *) obj;
arg = end; arg = end;
if (rebind) if (rebind)
break; break;
} }
} }
else if ((strchr (arg, '/') == NULL && option == 'S'))
{
lookup_or_install_new_server (&newserv);
}
break; break;
} }
case LOPT_REV_SERV: /* --rev-server */ case LOPT_REV_SERV: /* --rev-server */
{ {
char *string; char *string;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment