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
of --ipsets entries.
The --ipsets and --server lookup has been rewritten. It scales well with thousands
of --ipsets and --server entries.
## In progress
Still working on --server.
### In progress
Still need test and tweak.
[Contact me](mailto: weichen302@gmail.com)
......@@ -423,15 +423,15 @@ void free_dicttree (struct dict_node *node)
static inline int is_same_server(struct server *s1, struct server *s2)
{
if (memcmp(&s1->addr, &s2->addr, sizeof(union mysockaddr)) != 0)
return -1;
return 0;
if (strncmp(s1->interface, s2->interface, IF_NAMESIZE + 1) != 0)
return -1;
return 0;
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
......
......@@ -254,9 +254,6 @@ int main (int argc, char **argv)
ipset_init();
#endif
if (daemon->dh_special_domains == NULL)
daemon->dh_special_domains = new_dictnode(NULL, 0);
#if defined(HAVE_LINUX_NETWORK)
netlink_init();
#elif defined(HAVE_BSD_NETWORK)
......
......@@ -118,91 +118,111 @@ int send_from(int fd, int nowild, char *packet, size_t len,
return 1;
}
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)
/* search domain pattern for --address, --local, --server, --rebind-domain-ok
if matches --address, the address is stored in addrpp
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
domain to point to that name. We find the largest match to allow both
domain.org and sub.domain.org to exist. */
unsigned int namelen = strlen (qdomain);
struct server *serv;
unsigned int flags = 0;
unsigned int sflag;
struct dict_node *np;
struct special_domain *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);
unsigned int namelen = strlen (qdomain);
unsigned int flags = 0;
unsigned int sflag;
struct dict_node *np;
struct special_domain *obj;
*type = 0;
np = match_domain (daemon->dh_special_domains, qdomain);
if (np != NULL)
{
obj = (struct special_domain *) np->obj;
if (obj->domain_flags & SERV_NO_REBIND)
*norebind = 1;
*type |= SERV_HAS_DOMAIN;
// no server, domain is local only
if (obj->domain_flags & SERV_NO_ADDR) {
if (obj->domain_flags & SERV_NO_REBIND)
*norebind = 1;
// no server, domain is local only
if (obj->domain_flags & SERV_NO_ADDR)
{
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;
if (sflag & qtype)
{
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *) &serv->addr.in.sin_addr;
}
else if (obj->domain_flags & SERV_LITERAL_ADDRESS)
{
// --address and AF matches
sflag = obj->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
if (sflag & qtype)
{
flags = sflag;
if (obj->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *) &obj->addr.in.sin_addr;
#ifdef HAVE_IPV6
else
*addrpp = (struct all_addr *) &serv->addr.in6.sin6_addr;
else
*addrpp = (struct all_addr *) &obj->addr.in6.sin6_addr;
#endif
}
} else {
*fwdserv = obj->server;
flags = 0;
}
}
}
else
{
*type = 0; // use normal server
else if (obj->domain_flags & SERV_USE_RESOLV)
{
// --server=8.8.8.8
*type = 0; // use normal server
*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) &&
option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
if (flags == 0 && !(qtype & F_QUERY) &&
option_bool (OPT_NODOTS_LOCAL) && !strchr (qdomain, '.')
&& namelen != 0)
/* don't forward A or AAAA queries for simple names, except the empty name */
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;
if (flags)
{
int logflags = 0;
if (flags == F_NXDOMAIN || flags == F_NOERR)
logflags = F_NEG | qtype;
log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, NULL);
logflags = F_NEG | qtype;
log_query (logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp,
NULL);
}
else if ((*type) & SERV_USE_RESOLV)
{
*type = 0; /* use normal servers for this domain */
*type = 0; /* use normal servers for this domain */
*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,
struct all_addr *dst_addr, unsigned int dst_iface,
struct dns_header *header, size_t plen, time_t now,
......@@ -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
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)
fd = fwdserv->sfd->fd;
else
else
{
#ifdef HAVE_IPV6
if (fwdserv->addr.sa.sa_family == AF_INET6)
{
if (forward->rfd6 == NULL)
forward->rfd6 = allocate_rfd(AF_INET6);
forward->rfd6 = allocate_rfd (AF_INET6);
daemon->rfd_save = forward->rfd6;
fd = forward->rfd6->fd;
}
......@@ -405,30 +438,28 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#endif
{
if (forward->rfd4 == NULL)
forward->rfd4 = allocate_rfd(AF_INET);
forward->rfd4 = allocate_rfd (AF_INET);
daemon->rfd_save = forward->rfd4;
fd = forward->rfd4->fd;
}
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK))
if (option_bool (OPT_CONNTRACK))
{
unsigned int mark;
if (get_incoming_mark(&forward->source, &forward->dest, 0, &mark))
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
if (get_incoming_mark (&forward->source, &forward->dest, 0, &mark))
setsockopt (fd, SOL_SOCKET, SO_MARK, &mark,
sizeof (unsigned int));
}
#endif
}
if (sendto(fd, (char *)header, plen, 0,
&fwdserv->addr.sa,
sa_len(&fwdserv->addr)) == -1)
//TODO retry
if (sendto (fd, (char *) header, plen, 0,
&fwdserv->addr.sa, sa_len (&fwdserv->addr)) == -1)
{
retry_send();
retry_send ();
}
else
......@@ -436,27 +467,27 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
/* Keep info in case we want to re-send this packet */
daemon->srv_save = fwdserv;
daemon->packet_len = plen;
if (!gotname)
strcpy(daemon->namebuff, "query");
strcpy (daemon->namebuff, "query");
if (fwdserv->addr.sa.sa_family == AF_INET)
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&fwdserv->addr.in.sin_addr, NULL);
log_query (F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *) &fwdserv->addr.in.sin_addr, NULL);
#ifdef HAVE_IPV6
else
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&fwdserv->addr.in6.sin6_addr, NULL);
#endif
log_query (F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *) &fwdserv->addr.in6.sin6_addr, NULL);
#endif
fwdserv->queries++;
forwarded = 1;
forward->sentto = fwdserv;
if (forward->forwardall)
forward->forwardall++;
if (forward->forwardall)
forward->forwardall++;
}
if (forwarded)
return 1;
return 1;
/* could not send on, prepare to return */
header->id = htons(forward->orig_id);
free_frec(forward); /* cancel */
......
......@@ -1466,8 +1466,10 @@ void check_servers(void)
char *s1, *s2;
if (!(serv->flags & SERV_HAS_DOMAIN))
s1 = _("unqualified"), s2 = _("names");
/*
else if (strlen(serv->domain) == 0)
s1 = _("default"), s2 = "";
*/
else
s1 = _("domain"), s2 = serv->domain;
......
......@@ -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));
memset(serv, 0, sizeof(struct server));
serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
}
......@@ -2203,139 +2203,185 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
arg = comma;
} while (arg);
break;
case 'S': /* --server */
case LOPT_LOCAL: /* --local */
case 'A': /* --address */
case LOPT_NO_REBIND: /* --rebind-domain-ok */
//TODO fast hash lookup
case 'S': /* --server */
case LOPT_LOCAL: /* --local */
case 'A': /* --address */
case LOPT_NO_REBIND: /* --rebind-domain-ok */
{
unhide_metas(arg);
unhide_metas (arg);
char *start_addr;
char *start_addr, *s;
char *err;
struct server newlist;
struct server newserv;
struct dict_node *np;
struct special_domain *obj;
memset(&newlist, 0, sizeof(struct server));
memset (&newserv, 0, sizeof (struct server));
#ifdef HAVE_LOOP
newlist.uid = rand32();
newserv.uid = rand32 ();
#endif
if (arg == NULL)
break;
break;
if (daemon->dh_special_domains == NULL)
daemon->dh_special_domains = new_dictnode (NULL, 0);
// scan the address part first
// --xxxx=/example.org/ample.com/temple.net/address-of-server
// ^
// scan the address part first
// --xxxx=/example.org/ample.com/temple.net/address-of-server
// ^
start_addr = NULL;
if (strchr(arg, '/') == NULL) {
if (strchr (arg, '/') == NULL)
{
// --xxxx=example.org (only availabe for --rebind-domain-ok)
if (option == LOPT_NO_REBIND)
newlist.flags |= SERV_NO_REBIND;
newserv.flags |= SERV_NO_REBIND;
else if (option == 'S')
{
// --server=8.8.8.8
start_addr = arg;
}
} else {
for (start_addr = arg;
(start_addr = strchr(start_addr, '/')) != NULL; ) ;
}
else
{
for (s = arg; *s != '\0'; s++)
{
if (*s == '/')
start_addr = s;
}
start_addr++;
}
}
/* --xxxx=/example.org/# , here "#" means use standard server*/
if (start_addr != NULL) {
if (*start_addr == '#') {
newlist.flags |= SERV_USE_RESOLV;
/* --xxxx=/example.org/# , here "#" means use standard server */
if (start_addr != NULL)
{
if (*start_addr == '#')
{
newserv.flags |= SERV_USE_RESOLV;
}
else if (*start_addr == '\0')
/* --xxxx=/example.org/here-is-empty */
} else if (*start_addr == '\0') {
if (!(newlist.flags & SERV_NO_REBIND))
newlist.flags |= SERV_NO_ADDR; /* no server */
{
if (!(newserv.flags & SERV_NO_REBIND))
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 */
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)
ret_err(err);
ret_err (err);
}
}
}
}
// --server
if (servers_only && option == 'S')
newlist.flags |= SERV_FROM_FILE;
if (servers_only && option == 'S')
newserv.flags |= SERV_FROM_FILE;
// --rebind-domain-ok
if (option == LOPT_NO_REBIND)
newlist.flags |= SERV_NO_REBIND;
if (option == LOPT_NO_REBIND)
newserv.flags |= SERV_NO_REBIND;
// --address will be handled inside the domain dict_node
// the arg pattern can be
// --xxxx=example.org (only availabe for --rebind-domain-ok) or
// --xxxx=/example.org/ or
// --xxxx=/example.org/ample.com/temple.net/
if (*arg == '/' || option == LOPT_NO_REBIND)
{
int rebind = !(*arg == '/');
char *end = NULL;
if (!rebind)
arg++;
while (rebind || (end = split_chr(arg, '/')))
{
char *domain = NULL;
/* elide leading dots - they are implied in the search algorithm */
while (*arg == '.') arg++;
/* # matches everything and becomes a zero length domain string */
if (strcmp(arg, "#") == 0)
domain = "";
else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
option = '?';
if (*arg == '/' || option == LOPT_NO_REBIND)
{
int rebind = !(*arg == '/');
char *end = NULL;
if (!rebind)
arg++;
while (rebind || (end = split_chr (arg, '/')))
{
char *domain = NULL;
/* elide leading dots - they are implied in the search algorithm */
while (*arg == '.')
arg++;
//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) {
obj = opt_malloc(sizeof(struct special_domain));
memset(obj, 0, sizeof(struct special_domain));
if (np->obj == NULL)
{
obj = opt_malloc (sizeof (struct special_domain));
memset (obj, 0, sizeof (struct special_domain));
obj->domain_flags = 0;
} else {
}
else
{
obj = (struct special_domain *) np->obj;
}
}
if (option == 'A') {
obj->domain_flags = newserv.flags;
if (option == 'A')
{
obj->server = NULL;
obj->domain_flags = SERV_LITERAL_ADDRESS;
memcpy(&obj->addr, &newlist.addr, sizeof(union mysockaddr));
} else if (option == 'S') {
obj->domain_flags |= SERV_LITERAL_ADDRESS;
memcpy (&obj->addr, &newserv.addr, sizeof (union mysockaddr));
}
else if (option == 'S')
{
// pointer to one of servers in daemon->servers link list,
// 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
// will be used by forward
obj->domain_flags |= SERV_NO_REBIND;
}
}
if (option == LOPT_LOCAL) {
if (option == LOPT_LOCAL)
{
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;
arg = end;
if (rebind)
break;
}
}
arg = end;
if (rebind)
break;
}
}
else if ((strchr (arg, '/') == NULL && option == 'S'))
{
lookup_or_install_new_server (&newserv);
}
break;
}
case LOPT_REV_SERV: /* --rev-server */
{
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