]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Unix socket contribution
authorYuri Schaeffer <yuri@nlnetlabs.nl>
Tue, 28 Aug 2012 12:24:57 +0000 (12:24 +0000)
committerYuri Schaeffer <yuri@nlnetlabs.nl>
Tue, 28 Aug 2012 12:24:57 +0000 (12:24 +0000)
git-svn-id: file:///svn/unbound/trunk@2751 be551aaa-1e26-0410-a405-d3ace91eadb9

contrib/README
contrib/unbound_unixsock.diff [new file with mode: 0644]

index f5123fc6c8a1e5d4826b66dd0f2a2454cdb37a66..19abd054401a9cb8745820e1ef2f712ee5abdcfe 100644 (file)
@@ -15,3 +15,5 @@ distribution but may be helpful.
        a local-zone and local-data include file for unbound.conf.
 * unbound-host.nagios.patch: makes unbound-host return status that fits right
        in with the nagios monitoring framework.  Contributed by Migiel de Vos.
+* unbound_unixsock.diff: Add Unix socket support for unbound-control. 
+       Contributed by Ilya Bakulin, 2012-08-28.
diff --git a/contrib/unbound_unixsock.diff b/contrib/unbound_unixsock.diff
new file mode 100644 (file)
index 0000000..09d05d3
--- /dev/null
@@ -0,0 +1,305 @@
+diff --git a/daemon/remote.c b/daemon/remote.c
+index a2b2204..b6990f3 100644
+--- a/daemon/remote.c
++++ b/daemon/remote.c
+@@ -81,6 +81,11 @@
+ #ifdef HAVE_NETDB_H
+ #include <netdb.h>
+ #endif
++#ifdef HAVE_PWD_H
++#include <pwd.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#endif
+ /* just for portability */
+ #ifdef SQ
+@@ -235,7 +240,8 @@ void daemon_remote_delete(struct daemon_remote* rc)
+  * @return false on failure.
+  */
+ static int
+-add_open(const char* ip, int nr, struct listen_port** list, int noproto_is_err)
++add_open(const char* ip, int nr, struct listen_port** list, int noproto_is_err,
++      struct config_file* cfg)
+ {
+       struct addrinfo hints;
+       struct addrinfo* res;
+@@ -246,29 +252,74 @@ add_open(const char* ip, int nr, struct listen_port** list, int noproto_is_err)
+       snprintf(port, sizeof(port), "%d", nr);
+       port[sizeof(port)-1]=0;
+       memset(&hints, 0, sizeof(hints));
+-      hints.ai_socktype = SOCK_STREAM;
+-      hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+-      if((r = getaddrinfo(ip, port, &hints, &res)) != 0 || !res) {
+-#ifdef USE_WINSOCK
+-              if(!noproto_is_err && r == EAI_NONAME) {
+-                      /* tried to lookup the address as name */
+-                      return 1; /* return success, but do nothing */
++
++      if(ip[0] == '/') {
++              /* This looks like UNIX socket! */
++              fd = create_domain_accept_sock(ip);
++/*
++ * When unbound starts, it first creates a socket and then
++ * drops privs, so the socket is created as root user.
++ * This is fine, but we would like to set _unbound user group
++ * for this socket, and permissions should be 0660 so only
++ * root and _unbound group members can invoke unbound-control.
++ * The username used here is the same as username that unbound
++ * uses for its worker processes.
++ */
++
++/*
++ * Note: this code is an exact copy of code from daemon.c
++ * Normally this should be either wrapped into a function,
++ * or gui/gid values should be retrieved at config parsing time
++ * and then stored in configfile structure.
++ * This requires action from unbound developers!
++*/
++#ifdef HAVE_GETPWNAM
++              struct passwd *pwd = NULL;
++              uid_t uid;
++              gid_t gid;
++              /* initialize, but not to 0 (root) */
++              memset(&uid, 112, sizeof(uid));
++              memset(&gid, 112, sizeof(gid));
++              log_assert(cfg);
++
++              if(cfg->username && cfg->username[0]) {
++                      if((pwd = getpwnam(cfg->username)) == NULL)
++                              fatal_exit("user '%s' does not exist.",
++                                      cfg->username);
++                      uid = pwd->pw_uid;
++                      gid = pwd->pw_gid;
++                      endpwent();
+               }
++
++              chown(ip, 0, gid);
++              chmod(ip, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
++#endif
++      } else {
++              hints.ai_socktype = SOCK_STREAM;
++              hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
++              if((r = getaddrinfo(ip, port, &hints, &res)) != 0 || !res) {
++#ifdef USE_WINSOCK
++                      if(!noproto_is_err && r == EAI_NONAME) {
++                              /* tried to lookup the address as name */
++                              return 1; /* return success, but do nothing */
++                      }
+ #endif /* USE_WINSOCK */
+-                log_err("control interface %s:%s getaddrinfo: %s %s",
+-                      ip?ip:"default", port, gai_strerror(r),
++                      log_err("control interface %s:%s getaddrinfo: %s %s",
++                              ip?ip:"default", port, gai_strerror(r),
+ #ifdef EAI_SYSTEM
+                       r==EAI_SYSTEM?(char*)strerror(errno):""
+ #else
+                       ""
+ #endif
+                       );
+-              return 0;
++                      return 0;
++              }
++
++              /* open fd */
++              fd = create_tcp_accept_sock(res, 1, &noproto);
++              freeaddrinfo(res);
+       }
+-      /* open fd */
+-      fd = create_tcp_accept_sock(res, 1, &noproto);
+-      freeaddrinfo(res);
+       if(fd == -1 && noproto) {
+               if(!noproto_is_err)
+                       return 1; /* return success, but do nothing */
+@@ -305,7 +356,7 @@ struct listen_port* daemon_remote_open_ports(struct config_file* cfg)
+       if(cfg->control_ifs) {
+               struct config_strlist* p;
+               for(p = cfg->control_ifs; p; p = p->next) {
+-                      if(!add_open(p->str, cfg->control_port, &l, 1)) {
++                      if(!add_open(p->str, cfg->control_port, &l, 1, cfg)) {
+                               listening_ports_free(l);
+                               return NULL;
+                       }
+@@ -313,12 +364,12 @@ struct listen_port* daemon_remote_open_ports(struct config_file* cfg)
+       } else {
+               /* defaults */
+               if(cfg->do_ip6 &&
+-                      !add_open("::1", cfg->control_port, &l, 0)) {
++                      !add_open("::1", cfg->control_port, &l, 0, cfg)) {
+                       listening_ports_free(l);
+                       return NULL;
+               }
+               if(cfg->do_ip4 &&
+-                      !add_open("127.0.0.1", cfg->control_port, &l, 1)) {
++                      !add_open("127.0.0.1", cfg->control_port, &l, 1, cfg)) {
+                       listening_ports_free(l);
+                       return NULL;
+               }
+diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c
+index ea7ec3a..4cb04e2 100644
+--- a/services/listen_dnsport.c
++++ b/services/listen_dnsport.c
+@@ -55,6 +55,10 @@
+ #endif
+ #include <fcntl.h>
++#ifndef USE_WINSOCK
++#include <sys/un.h>
++#endif
++
+ /** number of queued TCP connections for listen() */
+ #define TCP_BACKLOG 5 
+@@ -376,6 +380,53 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
+ }
+ int
++create_domain_accept_sock(char *path) {
++      int s;
++      struct sockaddr_un unixaddr;
++
++#ifndef USE_WINSOCK
++      unixaddr.sun_len = sizeof(unixaddr);
++      unixaddr.sun_family = AF_UNIX;
++      strlcpy(unixaddr.sun_path, path, 104);
++
++      if((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
++              log_err("Cannot create UNIX socket %s (%s)",
++                      path, strerror(errno));
++              return -1;
++      }
++
++      if(unlink(path) && errno != ENOENT) {
++              /* The socket already exists and cannot be removed */
++              log_err("Cannot remove old UNIX socket %s (%s)",
++                      path, strerror(errno));
++              return -1;
++      }
++
++      if(bind(s, (struct sockaddr *) &unixaddr,
++              sizeof(struct sockaddr_un)) == -1) {
++              log_err("Cannot bind UNIX socket %s (%s)",
++                      path, strerror(errno));
++              return -1;
++      }
++
++      if(!fd_set_nonblock(s)) {
++              log_err("Cannot set non-blocking mode");
++              return -1;
++      }
++
++      if(listen(s, TCP_BACKLOG) == -1) {
++              log_err("can't listen: %s", strerror(errno));
++              return -1;
++      }
++
++      return s;
++#else
++      log_err("UNIX sockets are not supported");
++      return -1;
++#endif
++}
++
++int
+ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto)
+ {
+       int s;
+diff --git a/smallapp/unbound-control.c b/smallapp/unbound-control.c
+index a872f92..10631fd 100644
+--- a/smallapp/unbound-control.c
++++ b/smallapp/unbound-control.c
+@@ -59,6 +59,8 @@
+ #include "util/locks.h"
+ #include "util/net_help.h"
++#include <sys/un.h>
++
+ /** Give unbound-control usage, and exit (1). */
+ static void
+ usage()
+@@ -158,6 +160,7 @@ contact_server(const char* svr, struct config_file* cfg, int statuscmd)
+ {
+       struct sockaddr_storage addr;
+       socklen_t addrlen;
++      int addrfamily = 0;
+       int fd;
+       /* use svr or the first config entry */
+       if(!svr) {
+@@ -176,12 +179,21 @@ contact_server(const char* svr, struct config_file* cfg, int statuscmd)
+       if(strchr(svr, '@')) {
+               if(!extstrtoaddr(svr, &addr, &addrlen))
+                       fatal_exit("could not parse IP@port: %s", svr);
++      } else if(svr[0] == '/') {
++              struct sockaddr_un* unixsock = (struct sockaddr_un *) &addr;
++              unixsock->sun_family = AF_UNIX;
++              unixsock->sun_len = sizeof(unixsock);
++              strlcpy(unixsock->sun_path, svr, 104);
++              addrlen = sizeof(struct sockaddr_un);
++              addrfamily = AF_UNIX;
+       } else {
+               if(!ipstrtoaddr(svr, cfg->control_port, &addr, &addrlen))
+                       fatal_exit("could not parse IP: %s", svr);
+       }
+-      fd = socket(addr_is_ip6(&addr, addrlen)?AF_INET6:AF_INET, 
+-              SOCK_STREAM, 0);
++
++      if(addrfamily != AF_UNIX)
++              addrfamily = addr_is_ip6(&addr, addrlen)?AF_INET6:AF_INET;
++      fd = socket(addrfamily, SOCK_STREAM, 0);
+       if(fd == -1) {
+ #ifndef USE_WINSOCK
+               fatal_exit("socket: %s", strerror(errno));
+diff --git a/util/net_help.c b/util/net_help.c
+index b3136a3..5b5b4a3 100644
+--- a/util/net_help.c
++++ b/util/net_help.c
+@@ -45,6 +45,7 @@
+ #include "util/module.h"
+ #include "util/regional.h"
+ #include <fcntl.h>
++#include <sys/un.h>
+ #include <openssl/ssl.h>
+ #include <openssl/err.h>
+@@ -135,7 +136,7 @@ log_addr(enum verbosity_value v, const char* str,
+ {
+       uint16_t port;
+       const char* family = "unknown";
+-      char dest[100];
++      char dest[108];
+       int af = (int)((struct sockaddr_in*)addr)->sin_family;
+       void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
+       if(verbosity < v)
+@@ -148,15 +149,23 @@ log_addr(enum verbosity_value v, const char* str,
+               case AF_UNIX: family="unix"; break;
+               default: break;
+       }
+-      if(inet_ntop(af, sinaddr, dest, (socklen_t)sizeof(dest)) == 0) {
+-              strncpy(dest, "(inet_ntop error)", sizeof(dest));
++
++      if(af != AF_UNIX) {
++              if(inet_ntop(af, sinaddr, dest, (socklen_t)sizeof(dest)) == 0) {
++                      strncpy(dest, "(inet_ntop error)", sizeof(dest));
++              }
++              dest[sizeof(dest)-1] = 0;
++              port = ntohs(((struct sockaddr_in*)addr)->sin_port);
++              if(verbosity >= 4)
++                      verbose(v, "%s %s %s port %d (len %d)", str, family,
++                              dest, (int)port, (int)addrlen);
++              else    verbose(v, "%s %s port %d", str, dest, (int)port);
++      } else {
++              struct sockaddr_un* unixsock;
++              unixsock = (struct sockaddr_un *) addr;
++              strlcpy(dest, unixsock->sun_path, sizeof(dest));
++              verbose(v, "%s %s %s", str, family, dest);
+       }
+-      dest[sizeof(dest)-1] = 0;
+-      port = ntohs(((struct sockaddr_in*)addr)->sin_port);
+-      if(verbosity >= 4)
+-              verbose(v, "%s %s %s port %d (len %d)", str, family, dest, 
+-                      (int)port, (int)addrlen);
+-      else    verbose(v, "%s %s port %d", str, dest, (int)port);
+ }
+ int