From 33b344454b95ed99bad7d3fe4d493870923deaf6 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Wed, 20 Jan 2010 10:54:48 -0800 Subject: dnsmasq: Allow runtime changing of DNS servers via STDIN Signed-off-by: San Mehat --- src/Android.mk | 1 + src/dnsmasq.c | 55 ++++++++++++++++++++++++++++++- src/dnsmasq.h | 3 ++ src/log.c | 24 +++++++++++++- src/network.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 182 insertions(+), 2 deletions(-) diff --git a/src/Android.mk b/src/Android.mk index f68a09b..35f6dd6 100644 --- a/src/Android.mk +++ b/src/Android.mk @@ -11,6 +11,7 @@ LOCAL_MODULE := dnsmasq LOCAL_C_INCLUDES := external/dnsmasq/src LOCAL_CFLAGS := -O2 -g -W -Wall -D__ANDROID__ -DNO_IPV6 -DNO_TFTP -DNO_SCRIPT +LOCAL_SYSTEM_SHARED_LIBRARIES := libc libcutils include $(BUILD_EXECUTABLE) diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 5d05f28..1a9d808 100755 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -64,6 +64,10 @@ static void sig_handler(int sig); static void async_event(int pipe, time_t now); static void fatal_event(struct event_desc *ev); static void poll_resolv(void); +#ifdef __ANDROID__ +static int set_android_listeners(fd_set *set, int *maxfdp); +static int check_android_listeners(fd_set *set); +#endif int main (int argc, char **argv) { @@ -278,7 +282,9 @@ int main (int argc, char **argv) if (!(daemon->options & OPT_DEBUG)) { +#ifndef __ANDROID__ int nullfd; +#endif /* The following code "daemonizes" the process. See Stevens section 12.4 */ @@ -344,13 +350,15 @@ int main (int argc, char **argv) _exit(0); } } - + +#ifndef __ANDROID__ /* open stdout etc to /dev/null */ nullfd = open("/dev/null", O_RDWR); dup2(nullfd, STDOUT_FILENO); dup2(nullfd, STDERR_FILENO); dup2(nullfd, STDIN_FILENO); close(nullfd); +#endif } log_err = log_start(ent_pw, err_pipe[1]); @@ -577,6 +585,9 @@ int main (int argc, char **argv) t.tv_usec = 0; tp = &t; } +#ifdef __ANDROID__ + set_android_listeners(&rset, &maxfd); +#endif /* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */ if (daemon->tftp_trans || @@ -668,6 +679,10 @@ int main (int argc, char **argv) } check_dbus_listeners(&rset, &wset, &eset); #endif + +#ifdef __ANDROID__ + check_android_listeners(&rset); +#endif check_dns_listeners(&rset, now); @@ -960,6 +975,44 @@ void clear_cache_and_reload(time_t now) #endif } +#ifdef __ANDROID__ + +static int set_android_listeners(fd_set *set, int *maxfdp) { + FD_SET(STDIN_FILENO, set); + bump_maxfd(STDIN_FILENO, maxfdp); + return 0; +} + +static int check_android_listeners(fd_set *set) { + if (FD_ISSET(STDIN_FILENO, set)) { + char buffer[1024]; + int rc; + + if ((rc = read(STDIN_FILENO, buffer, sizeof(buffer) -1)) < 0) { + my_syslog(LOG_ERR, _("Error reading from stdin (%s)"), strerror(errno)); + return -1; + } + buffer[rc] = '\0'; + char *next = buffer; + char *cmd; + + if (!(cmd = strsep(&next, ":"))) { + my_syslog(LOG_ERR, _("Malformatted msg '%s'"), buffer); + return -1; + } + + if (!strcmp(buffer, "update_dns")) { + set_servers(&buffer[11]); + check_servers(); + } else { + my_syslog(LOG_ERR, _("Unknown cmd '%s'"), cmd); + return -1; + } + } + return 0; +} +#endif + static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp) { struct serverfd *serverfdp; diff --git a/src/dnsmasq.h b/src/dnsmasq.h index f204cbf..e2dedc6 100755 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -779,6 +779,9 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp); int random_sock(int family); void pre_allocate_sfds(void); int reload_servers(char *fname); +#ifdef __ANDROID__ +int set_servers(const char *servers); +#endif void check_servers(void); int enumerate_interfaces(); struct listener *create_wildcard_listeners(void); diff --git a/src/log.c b/src/log.c index 7ec491d..2fc0c6e 100755 --- a/src/log.c +++ b/src/log.c @@ -16,6 +16,10 @@ #include "dnsmasq.h" +#ifdef __ANDROID__ +#include +#endif + /* Implement logging to /dev/log asynchronously. If syslogd is making DNS lookups through dnsmasq, and dnsmasq blocks awaiting syslogd, then the two daemons can deadlock. We get around this @@ -261,6 +265,9 @@ void my_syslog(int priority, const char *format, ...) size_t len; pid_t pid = getpid(); char *func = ""; +#ifdef __ANDROID__ + int alog_lvl; +#endif if ((LOG_FACMASK & priority) == MS_TFTP) func = "-tftp"; @@ -278,6 +285,20 @@ void my_syslog(int priority, const char *format, ...) fputc('\n', stderr); } +#ifdef __ANDROID__ + if (priority <= LOG_ERR) + alog_lvl = ANDROID_LOG_ERROR; + else if (priority == LOG_WARNING) + alog_lvl = ANDROID_LOG_WARN; + else if (priority <= LOG_INFO) + alog_lvl = ANDROID_LOG_INFO; + else + alog_lvl = ANDROID_LOG_DEBUG; + va_start(ap, format); + __android_log_vprint(alog_lvl, "dnsmasq", format, ap); + va_end(ap); +#else + if (log_fd == -1) { /* fall-back to syslog if we die during startup or fail during running. */ @@ -369,7 +390,8 @@ void my_syslog(int priority, const char *format, ...) /* Have another go now */ log_write(); } - } + } +#endif } void set_log_writer(fd_set *set, int *maxfdp) diff --git a/src/network.c b/src/network.c index 68113fb..b0ffc6b 100755 --- a/src/network.c +++ b/src/network.c @@ -737,6 +737,107 @@ void check_servers(void) daemon->servers = ret; } +#ifdef __ANDROID__ +/* + * Takes a string in the format "1.2.3.4:1.2.3.4:..." - up to 1024 bytes in length + */ +int set_servers(const char *servers) +{ + char s[1024]; + struct server *old_servers = NULL; + struct server *new_servers = NULL; + struct server *serv; + + strncpy(s, servers, sizeof(s)); + + /* 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. */ + for (serv = daemon->servers; serv;) + { + struct server *tmp = serv->next; + if (serv->flags & SERV_FROM_RESOLV) + { + serv->next = old_servers; + old_servers = serv; + /* forward table rules reference servers, so have to blow them away */ + server_gone(serv); + } + else + { + serv->next = new_servers; + new_servers = serv; + } + serv = tmp; + } + + char *next = s; + char *saddr; + + while ((saddr = strsep(&next, ":"))) { + union mysockaddr addr, source_addr; + memset(&addr, 0, sizeof(addr)); + memset(&source_addr, 0, sizeof(source_addr)); + + if ((addr.in.sin_addr.s_addr = inet_addr(saddr)) != (in_addr_t) -1) + { +#ifdef HAVE_SOCKADDR_SA_LEN + source_addr.in.sin_len = addr.in.sin_len = sizeof(source_addr.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, saddr, &addr.in6.sin6_addr) > 0) + { +#ifdef HAVE_SOCKADDR_SA_LEN + source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6); +#endif + source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6; + addr.in6.sin6_port = htons(NAMESERVER_PORT); + 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 = whine_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->interface[0] = 0; + serv->sfd = NULL; + serv->flags = SERV_FROM_RESOLV; + serv->queries = serv->failed_queries = 0; + } + + /* Free any memory not used. */ + while (old_servers) + { + struct server *tmp = old_servers->next; + free(old_servers); + old_servers = tmp; + } + + daemon->servers = new_servers; + return 0; +} +#endif + /* Return zero if no servers found, in that case we keep polling. This is a protection against an update-time/write race on resolv.conf */ int reload_servers(char *fname) -- cgit v1.2.3