From: Emeric Brun Date: Fri, 22 Oct 2010 15:59:25 +0000 (+0200) Subject: [MEDIUM] Add supports of bind on unix sockets. X-Git-Tag: v1.5-dev8~403 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ed76092e10b2b542e99701a716912db7189ccb71;p=thirdparty%2Fhaproxy.git [MEDIUM] Add supports of bind on unix sockets. --- diff --git a/include/types/global.h b/include/types/global.h index 4469ef86ba..944d25e639 100644 --- a/include/types/global.h +++ b/include/types/global.h @@ -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 */ }; diff --git a/src/cfgparse.c b/src/cfgparse.c index 38bd69b5d7..59e238c782 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -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 .. */ + /* 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; diff --git a/src/frontend.c b/src/frontend.c index eea1ce0430..f51a3d9edb 100644 --- a/src/frontend.c +++ b/src/frontend.c @@ -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; } diff --git a/src/haproxy.c b/src/haproxy.c index 4859d092d9..074a11716d 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -114,6 +114,13 @@ struct global global = { } } }, + .unix_bind = { + .ux = { + .uid = -1, + .gid = -1, + .mode = 0, + } + }, .tune = { .bufsize = BUFSIZE, .maxrewrite = MAXREWRITE,