/* * SOCK_UNIX socket management * * Copyright 2000-2020 Willy Tarreau * * 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; either version * 2 of the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct proto_fam proto_fam_unix = { .name = "unix", .sock_domain = PF_UNIX, .sock_family = AF_UNIX, .sock_addrlen = sizeof(struct sockaddr_un), .l3_addrlen = sizeof(((struct sockaddr_un*)0)->sun_path), .addrcmp = sock_unix_addrcmp, .bind = sock_unix_bind_receiver, .get_src = sock_get_src, .get_dst = sock_get_dst, }; /* PLEASE NOTE for functions below: * * The address family SHOULD always be checked. In some cases a function will * be used in a situation where the address family is guaranteed (e.g. protocol * definitions), so the test may be avoided. This special case must then be * mentioned in the comment before the function definition. */ /* Compares two AF_UNIX sockaddr addresses. Returns 0 if they match or non-zero * if they do not match. It also supports ABNS socket addresses (those starting * with \0). For regular UNIX sockets however, this does explicitly support * matching names ending exactly with .XXXXX.tmp which are newly bound sockets * about to be replaced; this suffix is then ignored. Note that our UNIX socket * paths are always zero-terminated. */ int sock_unix_addrcmp(const struct sockaddr_storage *a, const struct sockaddr_storage *b) { const struct sockaddr_un *au = (const struct sockaddr_un *)a; const struct sockaddr_un *bu = (const struct sockaddr_un *)b; int idx, dot, idx2; if (a->ss_family != b->ss_family) return -1; if (a->ss_family != AF_UNIX) return -1; if (au->sun_path[0] != bu->sun_path[0]) return -1; if (au->sun_path[0] == 0) return memcmp(au->sun_path, bu->sun_path, sizeof(au->sun_path)); idx = 1; dot = 0; while (au->sun_path[idx] == bu->sun_path[idx]) { if (au->sun_path[idx] == 0) return 0; if (au->sun_path[idx] == '.') dot = idx; idx++; } /* Now we have a difference. It's OK if they are within or after a * sequence of digits following a dot, and are followed by ".tmp". * * make sure to perform the check against tempname if the compared * string is in "final" format (does not end with ".XXXX.tmp"). * * Examples: * /tmp/test matches with /tmp/test.1822.tmp * /tmp/test.1822.tmp matches with /tmp/test.XXXX.tmp */ if (au->sun_path[idx] == 0 || bu->sun_path[idx] == 0) { if (au->sun_path[idx] == '.' || bu->sun_path[idx] == '.') dot = idx; /* try to match against temp path */ else return -1; /* invalid temp path */ } if (!dot) return -1; /* First, check in path "a" */ if (au->sun_path[idx] != 0) { for (idx2 = dot + 1; idx2 && isdigit((unsigned char)au->sun_path[idx2]);) idx2++; if (strcmp(au->sun_path + idx2, ".tmp") != 0) return -1; } /* Then check in path "b" */ if (bu->sun_path[idx] != 0) { for (idx2 = dot + 1; idx2 && isdigit((unsigned char)bu->sun_path[idx2]); idx2++) ; if (strcmp(bu->sun_path + idx2, ".tmp") != 0) return -1; } /* OK that's a match */ return 0; } /* Binds receiver , and assigns rx->iocb and rx->owner as the callback and * context, respectively, with ->bind_thread as the thread mask. Returns an * error code made of ERR_* bits on failure or ERR_NONE on success. On failure, * an error message may be passed into . */ int sock_unix_bind_receiver(struct receiver *rx, char **errmsg) { char tempname[MAXPATHLEN]; char backname[MAXPATHLEN]; struct sockaddr_un addr; const char *path; int maxpathlen; int fd, err, ext, ret; /* ensure we never return garbage */ if (errmsg) *errmsg = 0; err = ERR_NONE; if (rx->flags & RX_F_BOUND) return ERR_NONE; if (rx->flags & RX_F_MUST_DUP) { /* this is a secondary receiver that is an exact copy of a * reference which must already be bound (or has failed). * We'll try to dup() the other one's FD and take it. We * try hard not to reconfigure the socket since it's shared. */ BUG_ON(!rx->shard_info); if (!(rx->shard_info->ref->flags & RX_F_BOUND)) { /* it's assumed that the first one has already reported * the error, let's not spam with another one, and do * not set ERR_ALERT. */ err |= ERR_RETRYABLE; goto bind_ret_err; } /* taking the other one's FD will result in it being marked * extern and being dup()ed. Let's mark the receiver as * inherited so that it properly bypasses all second-stage * setup and avoids being passed to new processes. */ rx->flags |= RX_F_INHERITED; rx->fd = rx->shard_info->ref->fd; } /* if no FD was assigned yet, we'll have to either find a compatible * one or create a new one. */ if (rx->fd == -1) rx->fd = sock_find_compatible_fd(rx); path = ((struct sockaddr_un *)&rx->addr)->sun_path; maxpathlen = MIN(MAXPATHLEN, sizeof(addr.sun_path)); /* if the listener already has an fd assigned, then we were offered the * fd by an external process (most likely the parent), and we don't want * to create a new socket. However we still want to set a few flags on * the socket. */ fd = rx->fd; ext = (fd >= 0); if (ext) goto fd_ready; if (path[0]) { ret = snprintf(tempname, maxpathlen, "%s.%d.tmp", path, pid); if (ret < 0 || ret >= sizeof(addr.sun_path)) { err |= ERR_FATAL | ERR_ALERT; memprintf(errmsg, "name too long for UNIX socket (limit usually 97)"); goto bind_return; } ret = snprintf(backname, maxpathlen, "%s.%d.bak", path, pid); if (ret < 0 || ret >= maxpathlen) { err |= ERR_FATAL | ERR_ALERT; memprintf(errmsg, "name too long for UNIX socket (limit usually 97)"); goto bind_return; } /* 2. clean existing orphaned entries */ if (unlink(tempname) < 0 && errno != ENOENT) { err |= ERR_FATAL | ERR_ALERT; memprintf(errmsg, "error when trying to unlink previous UNIX socket (%s)", strerror(errno)); goto bind_return; } if (unlink(backname) < 0 && errno != ENOENT) { err |= ERR_FATAL | ERR_ALERT; memprintf(errmsg, "error when trying to unlink previous UNIX socket (%s)", strerror(errno)); goto bind_return; } /* 3. backup existing socket */ if (link(path, backname) < 0 && errno != ENOENT) { err |= ERR_FATAL | ERR_ALERT; memprintf(errmsg, "error when trying to preserve previous UNIX socket (%s)", strerror(errno)); goto bind_return; } /* Note: this test is redundant with the snprintf one above and * will never trigger, it's just added as the only way to shut * gcc's painfully dumb warning about possibly truncated output * during strncpy(). Don't move it above or smart gcc will not * see it! */ if (strlen(tempname) >= sizeof(addr.sun_path)) { err |= ERR_FATAL | ERR_ALERT; memprintf(errmsg, "name too long for UNIX socket (limit usually 97)"); goto bind_return; } strncpy(addr.sun_path, tempname, sizeof(addr.sun_path) - 1); addr.sun_path[sizeof(addr.sun_path) - 1] = 0; } else { /* first char is zero, it's an abstract socket whose address * is defined by all the bytes past this zero. */ memcpy(addr.sun_path, path, sizeof(addr.sun_path)); } addr.sun_family = AF_UNIX; /* WT: shouldn't we use my_socketat(rx->netns) here instead ? */ fd = socket(rx->proto->fam->sock_domain, rx->proto->sock_type, rx->proto->sock_prot); if (fd < 0) { err |= ERR_FATAL | ERR_ALERT; memprintf(errmsg, "cannot create receiving socket (%s)", strerror(errno)); goto bind_return; } fd_ready: if (ext && fd < global.maxsock && fdtab[fd].owner) { /* This FD was already bound so this means that it was already * known and registered before parsing, hence it's an inherited * FD. The only reason why it's already known here is that it * has been registered multiple times (multiple listeners on the * same, or a "shards" directive on the line). There cannot be * multiple listeners on one FD but at least we can create a * new one from the original one. We won't reconfigure it, * however, as this was already done for the first one. */ fd = dup(fd); if (fd == -1) { err |= ERR_RETRYABLE | ERR_ALERT; memprintf(errmsg, "cannot dup() receiving socket (%s)", strerror(errno)); goto bind_return; } } if (fd >= global.maxsock) { err |= ERR_FATAL | ERR_ABORT | ERR_ALERT; memprintf(errmsg, "not enough free sockets (raise '-n' parameter)"); goto bind_close_return; } if (fd_set_nonblock(fd) == -1) { err |= ERR_FATAL | ERR_ALERT; memprintf(errmsg, "cannot make socket non-blocking"); goto bind_close_return; } if (!ext && bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { /* note that bind() creates the socket on the file system */ if (errno == EADDRINUSE) { /* the old process might still own it, let's retry */ err |= ERR_RETRYABLE | ERR_ALERT; memprintf(errmsg, "cannot bind UNIX socket (already in use)"); goto bind_close_return; } else { err |= ERR_FATAL | ERR_ALERT; memprintf(errmsg, "cannot bind UNIX socket (%s)", strerror(errno)); goto bind_close_return; } } /* and different of -1 will be used to change the socket owner. * If is not 0, it will be used to restrict access to the socket. * While it is known not to be portable on every OS, it's still useful * where it works. We also don't change permissions on abstract sockets. */ if (!ext && path[0] && (((rx->settings->ux.uid != -1 || rx->settings->ux.gid != -1) && (chown(tempname, rx->settings->ux.uid, rx->settings->ux.gid) == -1)) || (rx->settings->ux.mode != 0 && chmod(tempname, rx->settings->ux.mode) == -1))) { err |= ERR_FATAL | ERR_ALERT; memprintf(errmsg, "cannot change UNIX socket ownership (%s)", strerror(errno)); goto err_unlink_temp; } /* Point of no return: we are ready, we'll switch the sockets. We don't * fear losing the socket because we have a copy of it in * backname. Abstract sockets are not renamed. */ if (!ext && path[0] && rename(tempname, path) < 0) { err |= ERR_FATAL | ERR_ALERT; memprintf(errmsg, "cannot switch final and temporary UNIX sockets (%s)", strerror(errno)); goto err_rename; } /* Cleanup: only unlink if we didn't inherit the fd from the parent */ if (!ext && path[0]) unlink(backname); rx->fd = fd; rx->flags |= RX_F_BOUND; if (!path[0]) { /* ABNS sockets do not support suspend, and they conflict with * other ones (no reuseport), so they must always be unbound. */ rx->flags |= RX_F_NON_SUSPENDABLE; } fd_insert(fd, rx->owner, rx->iocb, rx->bind_tgroup, rx->bind_thread); /* for now, all regularly bound TCP listeners are exportable */ if (!(rx->flags & RX_F_INHERITED)) HA_ATOMIC_OR(&fdtab[fd].state, FD_EXPORTED); return err; err_rename: ret = rename(backname, path); if (ret < 0 && errno == ENOENT) unlink(path); err_unlink_temp: if (!ext && path[0]) unlink(tempname); close(fd); err_unlink_back: if (!ext && path[0]) unlink(backname); bind_return: if (errmsg && *errmsg) { if (!ext) { char *path_str; path_str = sa2str((struct sockaddr_storage *)&rx->addr, 0, 0); memprintf(errmsg, "%s [%s]", *errmsg, ((path_str) ? path_str : "")); ha_free(&path_str); } else memprintf(errmsg, "%s [fd %d]", *errmsg, fd); } bind_ret_err: return err; bind_close_return: close(fd); goto bind_return; }