Commit 352d236d authored by Chen Wei's avatar Chen Wei

--server mod build successfully

parent c2a3d340
......@@ -25,8 +25,10 @@
#define FNV_32_PRIME ((uint32_t)0x01000193)
#define max(A, B) ((A) > (B) ? (A) : (B))
static char buf[MAXDNAME];
/* prototypes */
static struct dict_node *add_or_replace_dictnode (struct dict_node *node, char *label);
static struct dict_node *add_or_lookup_dictnode (struct dict_node *node, char *label);
static struct dict_node *lookup_dictnode (struct dict_node *node, char *label);
static void add_dicttree (struct dict_node *node, struct dict_node *sub);
static void upsize_dicttree (struct dict_node *np);
......@@ -205,7 +207,7 @@ static void add_dicttree (struct dict_node *node, struct dict_node *sub)
/* add a new subnode to node, or update the attr of the subnode with same
* label
* return the subnode */
static struct dict_node *add_or_replace_dictnode (struct dict_node *node,
static struct dict_node *add_or_lookup_dictnode (struct dict_node *node,
char *label)
{
struct dict_node *np;
......@@ -254,13 +256,11 @@ static struct dict_node *lookup_dictnode (struct dict_node *node, char *label)
return NULL;
}
/* look up the whole domain pattern by step over DNS name hierarchy top down.
* for example, if the pattern is cn.debian.org, the lookup will start with
* org, then debian, then cn */
struct dict_node * match_domain_ipsets (struct dict_node *root, char *domain)
struct dict_node * match_domain(struct dict_node *root, char *domain)
{
char buf[MAXDNAME];
char *labels[MAXLABELS];
int i, label_num;
int len = strlen (domain);
......@@ -307,8 +307,8 @@ struct dict_node * match_domain_ipsets (struct dict_node *root, char *domain)
res = node;
}
if (res == NULL)
return NULL;
if (res == NULL)
return NULL;
return res;
}
......@@ -318,7 +318,6 @@ struct dict_node * match_domain_ipsets (struct dict_node *root, char *domain)
* com, then google, then cache */
struct dict_node * lookup_domain (struct dict_node *root, char *domain)
{
char buf[MAXDNAME];
char *labels[MAXLABELS];
int i, label_num;
int len = strlen (domain);
......@@ -354,9 +353,8 @@ struct dict_node * lookup_domain (struct dict_node *root, char *domain)
/* add a domain pattern in the form of google.com to root
* return the node with lowest hierarchy */
struct dict_node *add_domain (struct dict_node *root, char *domain)
struct dict_node *add_or_lookup_domain (struct dict_node *root, char *domain)
{
char buf[MAXDNAME];
char *labels[MAXLABELS];
int i, label_num;
int len = strlen (domain);
......@@ -383,7 +381,7 @@ struct dict_node *add_domain (struct dict_node *root, char *domain)
node = root;
for (i = label_num - 1; i >= 0; i--)
{
node = add_or_replace_dictnode (node, labels[i]);
node = add_or_lookup_dictnode (node, labels[i]);
}
return node;
......@@ -416,3 +414,54 @@ void free_dicttree (struct dict_node *node)
free (node);
}
/* temp area * */
/* only compare addr, source_addr, interface, and flags */
static inline int is_same_server(struct server *s1, struct server *s2)
{
if (memcmp(&s1->addr, &s2->addr, sizeof(union mysockaddr)) != 0)
return -1;
if (strncmp(s1->interface, s2->interface, IF_NAMESIZE + 1) != 0)
return -1;
if (s1->flags != s2->flags)
return -1;
return 0;
}
/* duplicate a struct server, but only copy addr, source_addr, interfaces, and
* flags
* return the allocated pointer */
static inline struct server *serverdup(struct server *src)
{
struct server *dst;
dst = safe_malloc(sizeof(struct server));
memcpy(dst, src, sizeof(struct server));
return dst;
}
struct server *lookup_or_install_new_server(struct server *serv)
{
struct server *res;
res = NULL;
for (res = daemon->servers; res != NULL; res = res->next) {
if (is_same_server(res, serv))
break;
}
if (res == NULL) {
res = serverdup(serv);
res->next = daemon->servers;
daemon->servers = res;
}
return res;
}
......@@ -254,6 +254,9 @@ 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)
......
......@@ -534,6 +534,13 @@ struct dict_node {
struct dict_node **sub;
};
struct special_domain {
struct server *server;
union mysockaddr addr;
int domain_flags;
};
struct ipsets_names {
char **sets; /* ipsets names end with NULL ptr */
int count;
......@@ -965,8 +972,11 @@ extern struct daemon {
struct bogus_addr *bogus_addr, *ignore_addr;
struct server *servers;
struct ipsets *ipsets;
struct dict_node *dh_ipsets;
struct dict_node *dh_ipsets_names;
struct dict_node *dh_special_domains;
int log_fac; /* log facility */
char *log_file; /* optional log file */
int max_logs; /* queue limit */
......@@ -1392,8 +1402,9 @@ int add_to_ipset(const char *setname, const struct all_addr *ipaddr, int flags,
/* dict.c */
struct dict_node *new_dictnode (char *label, int len);
struct dict_node *lookup_domain(struct dict_node *root, char *domain);
struct dict_node *match_domain_ipsets (struct dict_node *root, char *domain);
struct dict_node *add_domain (struct dict_node *root, char *domain);
struct dict_node *match_domain(struct dict_node *root, char *domain);
struct dict_node *add_or_lookup_domain (struct dict_node *root, char *domain);
struct server *lookup_or_install_new_server(struct server *serv);
void free_dicttree (struct dict_node *node);
/* helper.c */
......
......@@ -120,102 +120,63 @@ int send_from(int fd, int nowild, char *packet, size_t len,
}
static unsigned int search_servers(time_t now, struct all_addr **addrpp,
unsigned int qtype, char *qdomain, int *type, char **domain, int *norebind)
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);
unsigned int matchlen = 0;
struct server *serv;
unsigned int flags = 0;
for (serv = daemon->servers; serv; serv=serv->next)
/* domain matches take priority over NODOTS matches */
if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.') && namelen != 0)
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)
{
unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
*type = SERV_FOR_NODOTS;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
else if (serv->flags & SERV_LITERAL_ADDRESS)
{
if (sflag & qtype)
{
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
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)
*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;
#ifdef HAVE_IPV6
else
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif
}
else if (!flags || (flags & F_NXDOMAIN))
flags = F_NOERR;
}
else
*addrpp = (struct all_addr *) &serv->addr.in6.sin6_addr;
#endif
}
} else {
*fwdserv = obj->server;
flags = 0;
}
}
//TODO add fast hash lookup
else if (serv->flags & SERV_HAS_DOMAIN)
else
{
unsigned int domainlen = strlen(serv->domain);
char *matchstart = qdomain + namelen - domainlen;
if (namelen >= domainlen &&
hostname_isequal(matchstart, serv->domain) &&
(domainlen == 0 || namelen == domainlen || *(matchstart-1) == '.' ))
{
if (serv->flags & SERV_NO_REBIND)
*norebind = 1;
else
{
unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
/* implement priority rules for --address and --server for same domain.
--address wins if the address is for the correct AF
--server wins otherwise. */
if (domainlen != 0 && domainlen == matchlen)
{
if ((serv->flags & SERV_LITERAL_ADDRESS))
{
if (!(sflag & qtype) && flags == 0)
continue;
}
else
{
if (flags & (F_IPV4 | F_IPV6))
continue;
}
}
if (domainlen >= matchlen)
{
*type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND);
*domain = serv->domain;
matchlen = domainlen;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
else if (serv->flags & SERV_LITERAL_ADDRESS)
{
if (sflag & qtype)
{
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
else
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif
}
else if (!flags || (flags & F_NXDOMAIN))
flags = F_NOERR;
}
else
flags = 0;
}
}
}
*type = 0; // use normal server
*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)
/* don't forward A or AAAA queries for simple names, except the empty name */
......@@ -241,6 +202,7 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp,
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,
......@@ -251,6 +213,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
struct all_addr *addrp = NULL;
unsigned int flags = 0;
struct server *start = NULL;
struct server *fwdserv = NULL;
#ifdef HAVE_DNSSEC
void *hash = hash_questions(header, plen, daemon->namebuff);
#else
......@@ -322,7 +285,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
else
{
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind, &fwdserv);
if (!flags && !(forward = get_new_frec(now, NULL, 0)))
/* table full - server failure. */
......@@ -387,7 +350,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (!flags && forward)
{
struct server *firstsentto = start;
int forwarded = 0;
/* If a query is retried, use the log_id for the retry when logging the answer. */
......@@ -423,92 +385,75 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
#endif
while (1)
{
/* only send to servers dealing with our domain.
domain may be NULL, in which case server->domain
must be NULL also. */
if (type == (start->flags & SERV_TYPE) &&
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
!(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
{
int fd;
// 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. */
if (start->sfd)
fd = start->sfd->fd;
else
{
/* find server socket to use, may need to get random one. */
if (fwdserv->sfd)
fd = fwdserv->sfd->fd;
else
{
#ifdef HAVE_IPV6
if (start->addr.sa.sa_family == AF_INET6)
{
if (!forward->rfd6 &&
!(forward->rfd6 = allocate_rfd(AF_INET6)))
break;
daemon->rfd_save = forward->rfd6;
fd = forward->rfd6->fd;
}
else
if (fwdserv->addr.sa.sa_family == AF_INET6)
{
if (forward->rfd6 == NULL)
forward->rfd6 = allocate_rfd(AF_INET6);
daemon->rfd_save = forward->rfd6;
fd = forward->rfd6->fd;
}
else
#endif
{
if (!forward->rfd4 &&
!(forward->rfd4 = allocate_rfd(AF_INET)))
break;
daemon->rfd_save = forward->rfd4;
fd = forward->rfd4->fd;
}
{
if (forward->rfd4 == NULL)
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))
{
unsigned int mark;
if (get_incoming_mark(&forward->source, &forward->dest, 0, &mark))
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
/* Copy connection mark of incoming query to outgoing connection. */
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));
}
#endif
}
if (sendto(fd, (char *)header, plen, 0,
&start->addr.sa,
sa_len(&start->addr)) == -1)
{
if (retry_send())
continue;
}
else
{
/* Keep info in case we want to re-send this packet */
daemon->srv_save = start;
daemon->packet_len = plen;
if (!gotname)
strcpy(daemon->namebuff, "query");
if (start->addr.sa.sa_family == AF_INET)
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&start->addr.in.sin_addr, NULL);
}
if (sendto(fd, (char *)header, plen, 0,
&fwdserv->addr.sa,
sa_len(&fwdserv->addr)) == -1)
{
retry_send();
}
else
{
/* 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");
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);
#ifdef HAVE_IPV6
else
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&start->addr.in6.sin6_addr, NULL);
else
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&fwdserv->addr.in6.sin6_addr, NULL);
#endif
start->queries++;
forwarded = 1;
forward->sentto = start;
if (!forward->forwardall)
break;
forward->forwardall++;
}
}
if (!(start = start->next))
start = daemon->servers;
if (start == firstsentto)
break;
}
fwdserv->queries++;
forwarded = 1;
forward->sentto = fwdserv;
if (forward->forwardall)
forward->forwardall++;
}
if (forwarded)
return 1;
......@@ -543,7 +488,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
#ifdef HAVE_IPSET
if (daemon->dh_ipsets && extract_request(header, n, daemon->namebuff, NULL))
{
np = match_domain_ipsets(daemon->dh_ipsets, daemon->namebuff);
np = match_domain(daemon->dh_ipsets, daemon->namebuff);
if (np != NULL)
{
obj = (struct ipsets_names *) np->obj;
......@@ -707,6 +652,7 @@ void reply_query(int fd, int family, time_t now)
if (!(server->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
sockaddr_isequal(&server->addr, &serveraddr))
break;
/* TODO server may also come from the list for special domains */
if (!server)
return;
......@@ -1677,7 +1623,7 @@ unsigned char *tcp_request(int confd, time_t now,
/* largest field in header is 16-bits, so this is still sufficiently aligned */
struct dns_header *header = (struct dns_header *)payload;
u16 *length = (u16 *)packet;
struct server *last_server;
struct server *last_server, *fwdserv;
struct in_addr dst_addr_4;
union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr);
......@@ -1810,7 +1756,7 @@ unsigned char *tcp_request(int confd, time_t now,
}
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind, &fwdserv);
if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
last_server = daemon->servers;
......
......@@ -2210,11 +2210,74 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_NO_REBIND: /* --rebind-domain-ok */
//TODO fast hash lookup
{
struct server *serv, *newlist = NULL;
unhide_metas(arg);
char *start_addr;
char *err;
struct server newlist;
struct dict_node *np;
struct special_domain *obj;
memset(&newlist, 0, sizeof(struct server));
#ifdef HAVE_LOOP
newlist.uid = rand32();
#endif
if (arg == NULL)
break;
// scan the address part first
// --xxxx=/example.org/ample.com/temple.net/address-of-server
// ^
start_addr = NULL;
if (strchr(arg, '/') == NULL) {
// --xxxx=example.org (only availabe for --rebind-domain-ok)
if (option == LOPT_NO_REBIND)
newlist.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; ) ;
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-is-empty */
} else if (*start_addr == '\0') {
if (!(newlist.flags & SERV_NO_REBIND))
newlist.flags |= SERV_NO_ADDR; /* no server */
} 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);
if (err)
ret_err(err);
}
}
// --server
if (servers_only && option == 'S')
newlist.flags |= SERV_FROM_FILE;
if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
// --rebind-domain-ok
if (option == LOPT_NO_REBIND)
newlist.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;
......@@ -2230,75 +2293,49 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
domain = "";
else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
option = '?';
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
serv->next = newlist;
newlist = serv;
serv->domain = domain;
serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
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));
obj->domain_flags = 0;
} else {
obj = (struct special_domain *) np->obj;
}
if (option == 'A') {
obj->server = NULL;
obj->domain_flags = SERV_LITERAL_ADDRESS;
memcpy(&obj->addr, &newlist.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);
}
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) {
obj->domain_flags |= SERV_NO_ADDR;
}
//newlist.flags |= domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
np->obj = (void *) obj;
arg = end;
if (rebind)
break;
}
if (!newlist)
ret_err(gen_err);
}
else
{
newlist = opt_malloc(sizeof(struct server));
memset(newlist, 0, sizeof(struct server));
#ifdef HAVE_LOOP
newlist->uid = rand32();
#endif
}
if (servers_only && option == 'S')
newlist->flags |= SERV_FROM_FILE;
if (option == 'A')
{
newlist->flags |= SERV_LITERAL_ADDRESS;
if (!(newlist->flags & SERV_TYPE))
ret_err(gen_err);
}
else if (option == LOPT_NO_REBIND)
newlist->flags |= SERV_NO_REBIND;
if (!arg || !*arg)
{
if (!(newlist->flags & SERV_NO_REBIND))
newlist->flags |= SERV_NO_ADDR; /* no server */
if (newlist->flags & SERV_LITERAL_ADDRESS)
ret_err(gen_err);
}
else if (strcmp(arg, "#") == 0)
{
newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
if (newlist->flags & SERV_LITERAL_ADDRESS)
ret_err(gen_err);
}
else
{
char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
if (err)
ret_err(err);
}
serv = newlist;
while (serv->next)
{
serv->next->flags = serv->flags;
serv->next->addr = serv->addr;
serv->next->source_addr = serv->source_addr;
strcpy(serv->next->interface, serv->interface);
serv = serv->next;
}
serv->next = daemon->servers;
daemon->servers = newlist;
break;
break;
}
case LOPT_REV_SERV: /* --rev-server */
{
char *string;
......@@ -2368,7 +2405,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
option = '?';
if (domain != NULL)
np = add_domain (daemon->dh_ipsets, domain);
np = add_or_lookup_domain (daemon->dh_ipsets, domain);
arg = end;
}
......
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