/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley This program 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; version 2 dated June, 1991. This program 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. */ /* Author's email: simon@thekelleys.org.uk */ #include "dnsmasq.h" /* Foxconn added start JamesHsu 08/05/2014 */ /* dnsmasq interface */ struct iname *vpn_iface; /* Foxconn added end JamesHsu 08/05/2014 */ static struct irec *add_iface(struct daemon *daemon, struct irec *list, char *name, union mysockaddr *addr) { struct irec *iface; struct iname *tmp; /* check blacklist */ if (daemon->if_except) for (tmp = daemon->if_except; tmp; tmp = tmp->next) if (tmp->name && strcmp(tmp->name, name) == 0) { /* record address of named interfaces, for TCP access control */ tmp->addr = *addr; return list; } /* we may need to check the whitelist */ if (daemon->if_names || daemon->if_addrs) { int found = 0; for (tmp = daemon->if_names; tmp; tmp = tmp->next) if (tmp->name && (strcmp(tmp->name, name) == 0)) { tmp->addr = *addr; found = tmp->used = 1; } for (tmp = daemon->if_addrs; tmp; tmp = tmp->next) if (sockaddr_isequal(&tmp->addr, addr)) found = tmp->used = 1; if (!found) return list; } /* check whether the interface IP has been added already it is possible to have multiple interfaces with the same address */ for (iface = list; iface; iface = iface->next) if (sockaddr_isequal(&iface->addr, addr)) break; if (iface) return list; /* If OK, add it to the head of the list */ iface = safe_malloc(sizeof(struct irec)); iface->addr = *addr; iface->next = list; return iface; } struct irec *enumerate_interfaces(struct daemon *daemon) { struct irec *iface = NULL; char *buf, *ptr; struct ifreq *ifr = NULL; struct ifconf ifc; int lastlen = 0; int len = 20 * sizeof(struct ifreq); int fd = socket(PF_INET, SOCK_DGRAM, 0); /* Foxconn added start, James hsu, 08/05/2014, */ /* Note the corrent interface */ vpn_iface= daemon->if_names; /* Foxconn added end, James hsu, 08/05/2014, */ if (fd == -1) die ("cannot create socket to enumerate interfaces: %s", NULL); while (1) { buf = safe_malloc(len); ifc.ifc_len = len; ifc.ifc_buf = buf; if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { if (errno != EINVAL || lastlen != 0) die ("ioctl error while enumerating interfaces: %s", NULL); } else { if (ifc.ifc_len == lastlen) break; /* got a big enough buffer now */ lastlen = ifc.ifc_len; } len += 10*sizeof(struct ifreq); free(buf); } for (ptr = buf; ptr < buf + len; ) { union mysockaddr addr; #ifdef HAVE_SOCKADDR_SA_LEN /* subsequent entries may not be aligned, so copy into an aligned buffer to avoid nasty complaints about unaligned accesses. */ int ifr_len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE; if (!(ifr = realloc(ifr, ifr_len))) die("cannot allocate buffer", NULL); memcpy(ifr, ptr, ifr_len); ptr += ifr_len; #else ifr = (struct ifreq *)ptr; ptr += sizeof(struct ifreq); #endif /* copy address since getting flags overwrites */ if (ifr->ifr_addr.sa_family == AF_INET) { addr.in = *((struct sockaddr_in *) &ifr->ifr_addr); addr.in.sin_port = htons(daemon->port); } #ifdef HAVE_IPV6 else if (ifr->ifr_addr.sa_family == AF_INET6) { #ifdef HAVE_BROKEN_SOCKADDR_IN6 addr.in6 = *((struct my_sockaddr_in6 *) &ifr->ifr_addr); #else addr.in6 = *((struct sockaddr_in6 *) &ifr->ifr_addr); #endif addr.in6.sin6_port = htons(daemon->port); addr.in6.sin6_flowinfo = htonl(0); } #endif else continue; /* unknown address family */ if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0) die("ioctl error getting interface flags: %m", NULL); /* If we are restricting the set of interfaces to use, make sure that loopback interfaces are in that set. */ if (daemon->if_names && (ifr->ifr_flags & IFF_LOOPBACK)) { struct iname *lo; for (lo = daemon->if_names; lo; lo = lo->next) if (lo->name && strcmp(lo->name, ifr->ifr_name) == 0) { lo->isloop = 1; break; } if (!lo) { lo = safe_malloc(sizeof(struct iname)); lo->name = safe_string_alloc(ifr->ifr_name); lo->isloop = lo->used = 1; lo->next = daemon->if_names; daemon->if_names = lo; } } iface = add_iface(daemon, iface, ifr->ifr_name, &addr); #if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6) /* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */ /* This code snarfed from net-tools 1.60 and certainly linux specific, though it shouldn't break on other Unices, and their SIOGIFCONF might work. */ { FILE *f = fopen(IP6INTERFACES, "r"); int found = 0; union mysockaddr addr6; if (f) { unsigned int plen, scope, flags, if_idx; char devname[20], addrstring[32]; while (fscanf(f, "%32s %02x %02x %02x %02x %20s\n", addrstring, &if_idx, &plen, &scope, &flags, devname) != EOF) { if (strcmp(devname, ifr->ifr_name) == 0) { int i; unsigned char *addr6p = (unsigned char *) &addr6.in6.sin6_addr; memset(&addr6, 0, sizeof(addr6)); addr6.sa.sa_family = AF_INET6; for (i=0; i<16; i++) { unsigned int byte; sscanf(addrstring+i+i, "%02x", &byte); addr6p[i] = byte; } addr6.in6.sin6_port = htons(daemon->port); addr6.in6.sin6_flowinfo = htonl(0); addr6.in6.sin6_scope_id = htonl(scope); found = 1; break; } } fclose(f); } if (found) iface = add_iface(daemon, iface, ifr->ifr_name, &addr6); } #endif /* LINUX */ } if (buf) free(buf); #ifdef HAVE_SOCKADDR_SA_LEN if (ifr) free(ifr); #endif close(fd); return iface; } #ifdef HAVE_IPV6 static int create_ipv6_listener(struct listener **link, int port) { union mysockaddr addr; int tcpfd, fd, flags, save; struct listener *l; int opt = 1; addr.in6.sin6_family = AF_INET6; addr.in6.sin6_addr = in6addr_any; addr.in6.sin6_port = htons(port); addr.in6.sin6_flowinfo = htonl(0); #ifdef HAVE_SOCKADDR_SA_LEN addr.in6.sin6_len = sizeof(struct sockaddr_in6); #endif /* Foxconn added start, James hsu, 08/05/2014, Dino modified 11/26/2014 */ /* when interface = tun ,don't consider ipv6*/ if (strncmp(vpn_iface->name, "tun", 3)==0) { return 1; } /* Foxconn added start, James hsu, 08/05/2014, Dino modified 11/26/2014 */ /* No error of the kernel doesn't support IPv6 */ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) return (errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT || errno == EINVAL); if ((tcpfd = socket(AF_INET6, SOCK_STREAM, 0)) == -1) { save = errno; close(fd); errno = save; return 0; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || setsockopt(fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 || setsockopt(tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 || (flags = fcntl(tcpfd, F_GETFL, 0)) == -1 || fcntl(tcpfd, F_SETFL, flags | O_NONBLOCK) == -1 || #ifdef IPV6_RECVPKTINFO setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1 || #else setsockopt(fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1 || #endif bind(tcpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1 || listen(tcpfd, 5) == -1 || bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1) { save = errno; close(fd); close(tcpfd); errno = save; return 0; } l = safe_malloc(sizeof(struct listener)); l->fd = fd; l->tcpfd = tcpfd; l->family = AF_INET6; l->next = NULL; *link = l; return 1; } #endif extern int bind_addr; /* Foxconn added pling 08/26/2013 */ struct listener *create_wildcard_listeners(int port) { #if !(defined(IP_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR))) return NULL; #else union mysockaddr addr; int opt = 1; struct listener *l, *l6 = NULL; int flags; int tcpfd, fd; addr.in.sin_family = AF_INET; //addr.in.sin_addr.s_addr = INADDR_ANY; /* Foxconn added start pling 08/26/2013 */ /* Bind to LAN IP address, so it will not receive * any loopback DNS queries */ if (bind_addr) addr.in.sin_addr.s_addr = bind_addr; /* Foxconn added end pling 08/26/2013 */ addr.in.sin_port = htons(port); #ifdef HAVE_SOCKADDR_SA_LEN addr.in.sin_len = sizeof(struct sockaddr_in); #endif if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) return NULL; if ((tcpfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { close (fd); return NULL; } if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || bind(tcpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1 || listen(tcpfd, 5) == -1 || (flags = fcntl(tcpfd, F_GETFL, 0)) == -1 || fcntl(tcpfd, F_SETFL, flags | O_NONBLOCK) == -1 || #ifdef HAVE_IPV6 !create_ipv6_listener(&l6, port) || #endif setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || #if defined(IP_PKTINFO) setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 || #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 || setsockopt(fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 || #endif bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1) { close(fd); close(tcpfd); return NULL; } l = safe_malloc(sizeof(struct listener)); l->family = AF_INET; l->fd = fd; l->tcpfd = tcpfd; l->next = l6; return l; #endif } struct listener *create_bound_listeners(struct irec *interfaces, int port) { struct listener *listeners = NULL; struct irec *iface; int flags = port, opt = 1; /* Create bound listeners only for IPv4, IPv6 always binds the wildcard */ #ifdef HAVE_IPV6 if (!create_ipv6_listener(&listeners, port)) die("failed to to create listening socket: %s", NULL); #endif for (iface = interfaces ;iface; iface = iface->next) if (iface->addr.sa.sa_family == AF_INET) { /* Foxconn added start pling 08/26/2013 */ /* Only listen on LAN interface, but not others */ if (bind_addr != 0 && iface->addr.in.sin_addr.s_addr != bind_addr) { printf("bypass unwanted i/f (0x%08x)\n", iface->addr.in.sin_addr.s_addr); continue; } /* Foxconn added end pling 08/26/2013 */ struct listener *new = safe_malloc(sizeof(struct listener)); new->family = iface->addr.sa.sa_family; new->next = listeners; listeners = new; if ((new->tcpfd = socket(iface->addr.sa.sa_family, SOCK_STREAM, 0)) == -1 || (new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1 || setsockopt(new->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || setsockopt(new->tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || /* See Stevens 16.6 */ (flags = fcntl(new->tcpfd, F_GETFL, 0)) == -1 || fcntl(new->tcpfd, F_SETFL, flags | O_NONBLOCK) == -1 || bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1 || bind(new->fd, &iface->addr.sa, sa_len(&iface->addr)) == -1 || listen(new->tcpfd, 5) == -1) die("failed to to create listening socket: %s", NULL); } return listeners; } struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds) { struct serverfd *sfd; /* may have a suitable one already */ for (sfd = *sfds; sfd; sfd = sfd->next ) if (sockaddr_isequal(&sfd->source_addr, addr)) return sfd; /* need to make a new one. */ errno = ENOMEM; /* in case malloc fails. */ if (!(sfd = malloc(sizeof(struct serverfd)))) return NULL; if ((sfd->fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0)) == -1) { free(sfd); return NULL; } if (bind(sfd->fd, (struct sockaddr *)addr, sa_len(addr)) == -1) { int errsave = errno; /* save error from bind. */ close(sfd->fd); free(sfd); errno = errsave; return NULL; } sfd->source_addr = *addr; sfd->next = *sfds; *sfds = sfd; return sfd; } void check_servers(struct daemon *daemon, struct irec *interfaces) { char addrbuff[ADDRSTRLEN]; struct irec *iface; struct server *new, *tmp, *ret = NULL; int port = 0; /* forward table rules reference servers, so have to blow them away */ forward_init(0); daemon->last_server = NULL; for (new = daemon->servers; new; new = tmp) { tmp = new->next; if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR))) { #ifdef HAVE_IPV6 if (new->addr.sa.sa_family == AF_INET) { inet_ntop(AF_INET, &new->addr.in.sin_addr, addrbuff, ADDRSTRLEN); port = ntohs(new->addr.in.sin_port); } else if (new->addr.sa.sa_family == AF_INET6) { inet_ntop(AF_INET6, &new->addr.in6.sin6_addr, addrbuff, ADDRSTRLEN); port = ntohs(new->addr.in6.sin6_port); } #else strcpy(addrbuff, inet_ntoa(new->addr.in.sin_addr)); port = ntohs(new->addr.in.sin_port); #endif for (iface = interfaces; iface; iface = iface->next) if (sockaddr_isequal(&new->addr, &iface->addr)) break; /* Foxconn added start pling 08/26/2013 */ /* Don't load these special addresses from resolv.conf * to avoid DNS queries loopback to dnsmasq itself */ if (strcmp(addrbuff, "0.0.0.0") == 0 || strcmp(addrbuff, "127.0.0.1") == 0 || strcmp(addrbuff, "255.255.255.255") == 0 || strlen(addrbuff) < 7) { #ifdef USE_SYSLOG syslog(LOG_WARNING, "ignoring nameserver %s - invalid address", addrbuff); #endif printf("ignoring nameserver %s - invalid address\n", addrbuff); free(new); continue; } /* Foxconn added end pling 08/26/2013 */ if (iface) { #ifdef USE_SYSLOG /* foxconn wklin added, 08/13/2007 */ syslog(LOG_WARNING, "ignoring nameserver %s - local interface", addrbuff); #endif free(new); continue; } /* Do we need a socket set? */ if (!new->sfd && !(new->sfd = allocate_sfd(&new->source_addr, &daemon->sfds))) { #ifdef USE_SYSLOG /* foxconn wklin added, 08/13/2007 */ syslog(LOG_WARNING, "ignoring nameserver %s - cannot make/bind socket: %m", addrbuff); #endif free(new); continue; } } /* reverse order - gets it right. */ new->next = ret; ret = new; if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS)) { char *s1, *s2; if (new->flags & SERV_HAS_DOMAIN) s1 = "domain", s2 = new->domain; else s1 = "unqualified", s2 = "domains"; #ifdef USE_SYSLOG /* foxconn wklin added, 08/13/2007 */ if (new->flags & SERV_NO_ADDR) syslog(LOG_INFO, "using local addresses only for %s %s", s1, s2); else if (!(new->flags & SERV_LITERAL_ADDRESS)) syslog(LOG_INFO, "using nameserver %s#%d for %s %s", addrbuff, port, s1, s2); #endif } #ifdef USE_SYSLOG /* foxconn wklin added, 08/13/2007 */ else syslog(LOG_INFO, "using nameserver %s#%d", addrbuff, port); #endif } daemon->servers = ret; } void reload_servers(char *fname, struct daemon *daemon) { FILE *f; char *line; struct server *old_servers = NULL; struct server *new_servers = NULL; struct server *serv = daemon->servers; /* move old servers to free list - we can reuse the memory and not risk malloc if there are the same or fewer new servers. Servers which were specced on the command line go to the new list. */ while (serv) { struct server *tmp = serv->next; if (serv->flags & SERV_FROM_RESOLV) { serv->next = old_servers; old_servers = serv; } else { serv->next = new_servers; new_servers = serv; } serv = tmp; } /* buff happens to be NAXDNAME long... */ f = fopen(fname, "r"); if (!f) { #ifdef USE_SYSLOG /* foxconn wklin added, 08/13/2007 */ syslog(LOG_ERR, "failed to read %s: %m", fname); #endif } else { #ifdef USE_SYSLOG /* foxconn wklin added, 08/13/2007 */ syslog(LOG_INFO, "reading %s", fname); #endif while ((line = fgets(daemon->namebuff, MAXDNAME, f))) { union mysockaddr addr, source_addr; char *token = strtok(line, " \t\n\r"); struct server *serv; if (!token || strcmp(token, "nameserver") != 0) continue; if (!(token = strtok(NULL, " \t\n\r"))) continue; #ifdef HAVE_IPV6 if (inet_pton(AF_INET, token, &addr.in.sin_addr)) #else if ((addr.in.sin_addr.s_addr = inet_addr(token)) != (in_addr_t) -1) #endif { #ifdef HAVE_SOCKADDR_SA_LEN source_addr.in.sin_len = addr.in.sin_len = sizeof(struct sockaddr_in); #endif source_addr.in.sin_family = addr.in.sin_family = AF_INET; addr.in.sin_port = htons(NAMESERVER_PORT); source_addr.in.sin_addr.s_addr = INADDR_ANY; source_addr.in.sin_port = htons(daemon->query_port); } #ifdef HAVE_IPV6 else if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr)) { #ifdef HAVE_SOCKADDR_SA_LEN source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(struct sockaddr_in6); #endif source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6; addr.in6.sin6_port = htons(NAMESERVER_PORT); source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = htonl(0); source_addr.in6.sin6_addr = in6addr_any; source_addr.in6.sin6_port = htons(daemon->query_port); } #endif /* IPV6 */ else continue; if (old_servers) { serv = old_servers; old_servers = old_servers->next; } else if (!(serv = malloc(sizeof (struct server)))) continue; /* this list is reverse ordered: it gets reversed again in check_servers */ serv->next = new_servers; new_servers = serv; serv->addr = addr; serv->source_addr = source_addr; serv->domain = NULL; serv->sfd = NULL; serv->flags = SERV_FROM_RESOLV; } fclose(f); } /* Free any memory not used. */ while(old_servers) { struct server *tmp = old_servers->next; free(old_servers); old_servers = tmp; } daemon->servers = new_servers; }