]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] Add supports of bind on unix sockets.
authorEmeric Brun <ebrun@exceliance.fr>
Fri, 22 Oct 2010 15:59:25 +0000 (17:59 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 9 Nov 2010 14:59:42 +0000 (15:59 +0100)
include/types/global.h
src/cfgparse.c
src/frontend.c
src/haproxy.c

index 4469ef86ba7ef20a31541fc7185ddf7aacaa20f7..944d25e6396776edfbd6587bbd8d442823dace83 100644 (file)
@@ -93,6 +93,15 @@ struct global {
                int server_rcvbuf; /* set server rcvbuf to this value if not null */
                int chksize;       /* check buffer size in bytes, defaults to BUFSIZE */
        } tune;
+       struct {
+               char *prefix;           /* path prefix of unix bind socket */
+               struct {                /* UNIX socket permissions */
+                       uid_t uid;      /* -1 to leave unchanged */
+                       gid_t gid;      /* -1 to leave unchanged */
+                       mode_t mode;    /* 0 to leave unchanged */
+                       int level;      /* access level (ACCESS_LVL_*) */
+               } ux;
+       } unix_bind;
        struct listener stats_sock; /* unix socket listener for statistics */
        struct proxy *stats_fe;     /* the frontend holding the stats settings */
 };
index 38bd69b5d7d7271e203cd062ae6d94ed28069e9f..59e238c78232f089218bdc36ec3a6e023a9b4db9 100644 (file)
@@ -53,6 +53,7 @@
 #include <proto/port_range.h>
 #include <proto/protocols.h>
 #include <proto/proto_tcp.h>
+#include <proto/proto_uxst.h>
 #include <proto/proto_http.h>
 #include <proto/proxy.h>
 #include <proto/server.h>
@@ -191,63 +192,87 @@ static int str2listener(char *str, struct proxy *curproxy)
                        *next++ = 0;
                }
 
-               /* 2) look for the addr/port delimiter, it's the last colon. */
-               if ((range = strrchr(str, ':')) == NULL) {
-                       Alert("Missing port number: '%s'\n", str);
-                       goto fail;
-               }           
+               if (*str == '/') {
+                       /* sun_path during a soft_stop rename is <unix_bind_prefix><path>.<pid>.<bak|tmp> */
+                       /* so compute max path */
+                       int prefix_path_len = global.unix_bind.prefix ? strlen(global.unix_bind.prefix) : 0;
+                       int max_path_len = (sizeof(((struct sockaddr_un *)&ss)->sun_path) - 1) - (prefix_path_len + 1 + 5 + 1 + 3);
 
-               *range++ = 0;
+                       if (strlen(str) > max_path_len) {
+                                Alert("Socket path '%s' too long (max %d)\n", str, max_path_len);
+                               goto fail;
+                       }
 
-               if (strrchr(str, ':') != NULL) {
-                       /* IPv6 address contains ':' */
                        memset(&ss, 0, sizeof(ss));
-                       ss.ss_family = AF_INET6;
-
-                       if (!inet_pton(ss.ss_family, str, &((struct sockaddr_in6 *)&ss)->sin6_addr)) {
-                               Alert("Invalid server address: '%s'\n", str);
-                               goto fail;
+                       ss.ss_family = AF_UNIX;
+                       if (global.unix_bind.prefix) {
+                               memcpy(((struct sockaddr_un *)&ss)->sun_path, global.unix_bind.prefix, prefix_path_len);
+                               strcpy(((struct sockaddr_un *)&ss)->sun_path+prefix_path_len, str);
                        }
+                       else {
+                               strcpy(((struct sockaddr_un *)&ss)->sun_path, str);
+                       }
+                       port = end = 0;
                }
                else {
-                       memset(&ss, 0, sizeof(ss));
-                       ss.ss_family = AF_INET;
-
-                       if (*str == '*' || *str == '\0') { /* INADDR_ANY */
-                               ((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY;
+                       /* 2) look for the addr/port delimiter, it's the last colon. */
+                       if ((range = strrchr(str, ':')) == NULL) {
+                               Alert("Missing port number: '%s'\n", str);
+                               goto fail;
                        }
-                       else if (!inet_pton(ss.ss_family, str, &((struct sockaddr_in *)&ss)->sin_addr)) {
-                               struct hostent *he;
-               
-                               if ((he = gethostbyname(str)) == NULL) {
-                                       Alert("Invalid server name: '%s'\n", str);
+
+                       *range++ = 0;
+
+                       if (strrchr(str, ':') != NULL) {
+                               /* IPv6 address contains ':' */
+                               memset(&ss, 0, sizeof(ss));
+                               ss.ss_family = AF_INET6;
+
+                               if (!inet_pton(ss.ss_family, str, &((struct sockaddr_in6 *)&ss)->sin6_addr)) {
+                                       Alert("Invalid server address: '%s'\n", str);
                                        goto fail;
                                }
-                               else
-                                       ((struct sockaddr_in *)&ss)->sin_addr =
-                                               *(struct in_addr *) *(he->h_addr_list);
                        }
-               }
+                       else {
+                               memset(&ss, 0, sizeof(ss));
+                               ss.ss_family = AF_INET;
 
-               /* 3) look for the port-end delimiter */
-               if ((c = strchr(range, '-')) != NULL) {
-                       *c++ = 0;
-                       end = atol(c);
-               }
-               else {
-                       end = atol(range);
-               }
+                               if (*str == '*' || *str == '\0') { /* INADDR_ANY */
+                                       ((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY;
+                               }
+                               else if (!inet_pton(ss.ss_family, str, &((struct sockaddr_in *)&ss)->sin_addr)) {
+                                       struct hostent *he;
 
-               port = atol(range);
+                                       if ((he = gethostbyname(str)) == NULL) {
+                                               Alert("Invalid server name: '%s'\n", str);
+                                               goto fail;
+                                       }
+                                       else
+                                               ((struct sockaddr_in *)&ss)->sin_addr =
+                                                       *(struct in_addr *) *(he->h_addr_list);
+                               }
+                       }
 
-               if (port < 1 || port > 65535) {
-                       Alert("Invalid port '%d' specified for address '%s'.\n", port, str);
-                       goto fail;
-               }
+                       /* 3) look for the port-end delimiter */
+                       if ((c = strchr(range, '-')) != NULL) {
+                               *c++ = 0;
+                               end = atol(c);
+                       }
+                       else {
+                               end = atol(range);
+                       }
 
-               if (end < 1 || end > 65535) {
-                       Alert("Invalid port '%d' specified for address '%s'.\n", end, str);
-                       goto fail;
+                       port = atol(range);
+
+                       if (port < 1 || port > 65535) {
+                               Alert("Invalid port '%d' specified for address '%s'.\n", port, str);
+                               goto fail;
+                       }
+
+                       if (end < 1 || end > 65535) {
+                               Alert("Invalid port '%d' specified for address '%s'.\n", end, str);
+                               goto fail;
+                       }
                }
 
                for (; port <= end; port++) {
@@ -259,13 +284,19 @@ static int str2listener(char *str, struct proxy *curproxy)
                        l->addr = ss;
                        l->state = LI_INIT;
 
-                       if (ss.ss_family == AF_INET6) {
-                               ((struct sockaddr_in6 *)(&l->addr))->sin6_port = htons(port);
-                               tcpv6_add_listener(l);
-                       } else {
+                       if(ss.ss_family == AF_INET) {
                                ((struct sockaddr_in *)(&l->addr))->sin_port = htons(port);
                                tcpv4_add_listener(l);
                        }
+                       else if (ss.ss_family == AF_INET6) {
+                               ((struct sockaddr_in6 *)(&l->addr))->sin6_port = htons(port);
+                               tcpv6_add_listener(l);
+                       }
+                       else {
+                               l->perm.ux.gid = l->perm.ux.uid = -1;
+                               l->perm.ux.mode = 0;
+                               uxst_add_listener(l);
+                       }
 
                        jobs++;
                        listeners++;
@@ -770,6 +801,86 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
                }
                global.pidfile = strdup(args[1]);
        }
+       else if (!strcmp(args[0], "unix-bind")) {
+               int cur_arg = 1;
+               while (*(args[cur_arg])) {
+                       if (!strcmp(args[cur_arg], "prefix")) {
+                               if (global.unix_bind.prefix != NULL) {
+                                       Alert("parsing [%s:%d] : unix-bind '%s' already specified. Continuing.\n", file, linenum, args[cur_arg]);
+                                       err_code |= ERR_ALERT;
+                                       cur_arg += 2;
+                                       continue;
+                               }
+
+                               if (*(args[cur_arg+1]) == 0) {
+                                       Alert("parsing [%s:%d] : unix_bind '%s' expects a path as an argument.\n", file, linenum, args[cur_arg]);
+                                       err_code |= ERR_ALERT | ERR_FATAL;
+                                       goto out;
+                               }
+                               global.unix_bind.prefix =  strdup(args[cur_arg+1]);
+                               cur_arg += 2;
+                               continue;
+                       }
+
+                       if (!strcmp(args[cur_arg], "mode")) {
+
+                               global.unix_bind.ux.mode = strtol(args[cur_arg + 1], NULL, 8);
+                                cur_arg += 2;
+                               continue;
+                       }
+
+                       if (!strcmp(args[cur_arg], "uid")) {
+
+                               global.unix_bind.ux.uid = atol(args[cur_arg + 1 ]);
+                                cur_arg += 2;
+                               continue;
+                        }
+
+                       if (!strcmp(args[cur_arg], "gid")) {
+
+                               global.unix_bind.ux.gid = atol(args[cur_arg + 1 ]);
+                                cur_arg += 2;
+                               continue;
+                        }
+
+                       if (!strcmp(args[cur_arg], "user")) {
+                               struct passwd *user;
+
+                               user = getpwnam(args[cur_arg + 1]);
+                               if (!user) {
+                                       Alert("parsing [%s:%d] : '%s' : '%s' unknown user.\n",
+                                               file, linenum, args[0], args[cur_arg + 1 ]);
+                                       err_code |= ERR_ALERT | ERR_FATAL;
+                                       goto out;
+                               }
+
+                               global.unix_bind.ux.uid = user->pw_uid;
+                               cur_arg += 2;
+                               continue;
+                        }
+
+                       if (!strcmp(args[cur_arg], "group")) {
+                               struct group *group;
+
+                               group = getgrnam(args[cur_arg + 1]);
+                               if (!group) {
+                                       Alert("parsing [%s:%d] : '%s' : '%s' unknown group.\n",
+                                               file, linenum, args[0], args[cur_arg + 1 ]);
+                                       err_code |= ERR_ALERT | ERR_FATAL;
+                                       goto out;
+                               }
+
+                               global.unix_bind.ux.gid = group->gr_gid;
+                               cur_arg += 2;
+                               continue;
+                       }
+
+                       Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'accept-proxy', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n",
+                             file, linenum, args[0]);
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       goto out;
+                }
+       }
        else if (!strcmp(args[0], "log")) {  /* syslog server address */
                struct logsrv logsrv;
                int facility, level, minlvl;
@@ -1302,7 +1413,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                if (warnifnotcap(curproxy, PR_CAP_FE, file, linenum, args[0], NULL))
                        err_code |= ERR_WARN;
 
-               if (strchr(args[1], ':') == NULL) {
+               if ( *(args[1]) != '/' && strchr(args[1], ':') == NULL) {
                        Alert("parsing [%s:%d] : '%s' expects [addr1]:port1[-end1]{,[addr]:port[-end]}... as arguments.\n",
                              file, linenum, args[0]);
                        err_code |= ERR_ALERT | ERR_FATAL;
@@ -1327,12 +1438,23 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                        new_listen = new_listen->next;
                }
 
+               /* Set default global rights and owner for unix bind  */
+               if (curproxy->listen->addr.ss_family == AF_UNIX) {
+                       memcpy(&(curproxy->listen->perm.ux), &(global.unix_bind.ux), sizeof(global.unix_bind.ux));
+               }
                cur_arg = 2;
                while (*(args[cur_arg])) {
                        if (!strcmp(args[cur_arg], "interface")) { /* specifically bind to this interface */
 #ifdef SO_BINDTODEVICE
                                struct listener *l;
 
+                               if (curproxy->listen->addr.ss_family == AF_UNIX) {
+                                       Alert("parsing [%s:%d] : '%s' : '%s' option not supported on unix sockets.\n",
+                                             file, linenum, args[0], args[cur_arg]);
+                                       err_code |= ERR_ALERT | ERR_FATAL;
+                                       goto out;
+                               }
+
                                if (!*args[cur_arg + 1]) {
                                        Alert("parsing [%s:%d] : '%s' : missing interface name.\n",
                                              file, linenum, args[0]);
@@ -1359,6 +1481,13 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                                struct listener *l;
                                int mss;
 
+                               if (curproxy->listen->addr.ss_family == AF_UNIX) {
+                                       Alert("parsing [%s:%d] : '%s' : '%s' option not supported on unix sockets.\n",
+                                             file, linenum, args[0], args[cur_arg]);
+                                       err_code |= ERR_ALERT | ERR_FATAL;
+                                       goto out;
+                               }
+
                                if (!*args[cur_arg + 1]) {
                                        Alert("parsing [%s:%d] : '%s' : missing MSS value.\n",
                                              file, linenum, args[0]);
@@ -1408,6 +1537,13 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
 #ifdef CONFIG_HAP_LINUX_TPROXY
                                struct listener *l;
 
+                               if (curproxy->listen->addr.ss_family == AF_UNIX) {
+                                       Alert("parsing [%s:%d] : '%s' : '%s' option not supported on unix sockets.\n",
+                                             file, linenum, args[0], args[cur_arg]);
+                                       err_code |= ERR_ALERT | ERR_FATAL;
+                                       goto out;
+                               }
+
                                for (l = curproxy->listen; l != last_listen; l = l->next)
                                        l->options |= LI_O_FOREIGN;
 
@@ -1483,6 +1619,93 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                                continue;
                        }
 
+                       if (!strcmp(args[cur_arg], "mode")) {
+
+                               if (curproxy->listen->addr.ss_family != AF_UNIX) {
+                                       Alert("parsing [%s:%d] : '%s' : '%s' option only supported on unix sockets.\n",
+                                             file, linenum, args[0], args[cur_arg]);
+                                       err_code |= ERR_ALERT | ERR_FATAL;
+                                       goto out;
+                               }
+
+                               curproxy->listen->perm.ux.mode = strtol(args[cur_arg + 1], NULL, 8);
+
+                               cur_arg += 2;
+                               continue;
+                       }
+
+                       if (!strcmp(args[cur_arg], "uid")) {
+
+                                if (curproxy->listen->addr.ss_family != AF_UNIX) {
+                                        Alert("parsing [%s:%d] : '%s' : '%s' option only supported on unix sockets.\n",
+                                                file, linenum, args[0], args[cur_arg]);
+                                        err_code |= ERR_ALERT | ERR_FATAL;
+                                        goto out;
+                                }
+
+                                curproxy->listen->perm.ux.uid = atol(args[cur_arg + 1 ]);
+                                cur_arg += 2;
+                                continue;
+                        }
+
+                       if (!strcmp(args[cur_arg], "gid")) {
+
+                                if (curproxy->listen->addr.ss_family != AF_UNIX) {
+                                        Alert("parsing [%s:%d] : '%s' : '%s' option only supported on unix sockets.\n",
+                                                file, linenum, args[0], args[cur_arg]);
+                                        err_code |= ERR_ALERT | ERR_FATAL;
+                                        goto out;
+                                }
+
+                                curproxy->listen->perm.ux.gid = atol(args[cur_arg + 1 ]);
+                                cur_arg += 2;
+                                continue;
+                        }
+
+                       if (!strcmp(args[cur_arg], "user")) {
+                               struct passwd *user;
+
+                               if (curproxy->listen->addr.ss_family != AF_UNIX) {
+                                       Alert("parsing [%s:%d] : '%s' : '%s' option only supported on unix sockets.\n",
+                                               file, linenum, args[0], args[cur_arg]);
+                                       err_code |= ERR_ALERT | ERR_FATAL;
+                                       goto out;
+                               }
+                               user = getpwnam(args[cur_arg + 1]);
+                               if (!user) {
+                                       Alert("parsing [%s:%d] : '%s' : '%s' unknown user.\n",
+                                               file, linenum, args[0], args[cur_arg + 1 ]);
+                                       err_code |= ERR_ALERT | ERR_FATAL;
+                                       goto out;
+                               }
+
+                               curproxy->listen->perm.ux.uid = user->pw_uid;
+                               cur_arg += 2;
+                               continue;
+                        }
+
+                       if (!strcmp(args[cur_arg], "group")) {
+                               struct group *group;
+
+                               if (curproxy->listen->addr.ss_family != AF_UNIX) {
+                                       Alert("parsing [%s:%d] : '%s' : '%s' option only supported on unix sockets.\n",
+                                               file, linenum, args[0], args[cur_arg]);
+                                       err_code |= ERR_ALERT | ERR_FATAL;
+                                       goto out;
+                               }
+                               group = getgrnam(args[cur_arg + 1]);
+                               if (!group) {
+                                       Alert("parsing [%s:%d] : '%s' : '%s' unknown group.\n",
+                                               file, linenum, args[0], args[cur_arg + 1 ]);
+                                       err_code |= ERR_ALERT | ERR_FATAL;
+                                       goto out;
+                               }
+
+                               curproxy->listen->perm.ux.gid = group->gr_gid;
+                               cur_arg += 2;
+                               continue;
+                        }
+
                        Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'accept-proxy', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n",
                              file, linenum, args[0]);
                        err_code |= ERR_ALERT | ERR_FATAL;
index eea1ce043046858b28bb54a4cdcf33ec87e2f6f7..f51a3d9edb03e0112b9580fad43a382c14c11739 100644 (file)
@@ -87,7 +87,7 @@ int frontend_accept(struct session *s)
        s->srv_error = default_srv_error;
 
        /* Adjust some socket options */
-       if (unlikely(setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) == -1)) {
+       if ((s->listener->addr.ss_family != AF_UNIX) && unlikely(setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) == -1)) {
                Alert("accept(): cannot set the socket in non blocking mode. Giving up\n");
                goto out_delete_cfd;
        }
index 4859d092d9643f52fd42cb9411f1579ab3e9b697..074a11716dd1534b91e28d1b4fcb3dda1c813ec3 100644 (file)
@@ -114,6 +114,13 @@ struct global global = {
                         }
                 }
        },
+       .unix_bind = {
+                .ux = {
+                        .uid = -1,
+                        .gid = -1,
+                        .mode = 0,
+                }
+       },
        .tune = {
                .bufsize = BUFSIZE,
                .maxrewrite = MAXREWRITE,