From: Lennart Poettering Date: Wed, 19 May 2010 23:13:43 +0000 (+0200) Subject: sd-daemon: add API to verify socket types X-Git-Tag: v1~308 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=7c394faa38de389638e19b19212ed50aca870e3c sd-daemon: add API to verify socket types --- diff --git a/src/initctl.c b/src/initctl.c index 9d8eceea5..407d32d93 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -280,6 +280,20 @@ static int server_init(Server *s, unsigned n_sockets) { for (i = 0; i < n_sockets; i++) { struct epoll_event ev; Fifo *f; + int fd; + + fd = SD_LISTEN_FDS_START+i; + + if ((r = sd_is_fifo(fd, NULL)) < 0) { + log_error("Failed to determine file descriptor type: %s", strerror(-r)); + goto fail; + } + + if (!r) { + log_error("Wrong file descriptor type."); + r = -EINVAL; + goto fail; + } if (!(f = new0(Fifo, 1))) { r = -ENOMEM; @@ -292,7 +306,7 @@ static int server_init(Server *s, unsigned n_sockets) { zero(ev); ev.events = EPOLLIN; ev.data.ptr = f; - if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, SD_LISTEN_FDS_START+i, &ev) < 0) { + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { r = -errno; fifo_free(f); log_error("Failed to add fifo fd to epoll object: %s", strerror(errno)); diff --git a/src/logger.c b/src/logger.c index e7c2be01e..c486a8acd 100644 --- a/src/logger.c +++ b/src/logger.c @@ -424,11 +424,25 @@ static int server_init(Server *s, unsigned n_sockets) { for (i = 0; i < n_sockets; i++) { struct epoll_event ev; + int fd; + + fd = SD_LISTEN_FDS_START+i; + + if ((r = sd_is_socket(fd, SOCK_STREAM, 1)) < 0) { + log_error("Failed to determine file descriptor type: %s", strerror(-r)); + goto fail; + } + + if (!r) { + log_error("Wrong file descriptor type."); + r = -EINVAL; + goto fail; + } zero(ev); ev.events = EPOLLIN; - ev.data.ptr = UINT_TO_PTR(SD_LISTEN_FDS_START+i); - if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, SD_LISTEN_FDS_START+i, &ev) < 0) { + ev.data.ptr = UINT_TO_PTR(fd); + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { r = -errno; log_error("Failed to add server fd to epoll object: %s", strerror(errno)); goto fail; diff --git a/src/sd-daemon.c b/src/sd-daemon.c index cc972dabd..8a7c9e8db 100644 --- a/src/sd-daemon.c +++ b/src/sd-daemon.c @@ -24,10 +24,15 @@ SOFTWARE. ***/ +#include +#include +#include +#include +#include #include #include -#include #include +#include #include "sd-daemon.h" @@ -94,3 +99,171 @@ finish: return r; #endif } + +int sd_is_fifo(int fd, const char *path) { + struct stat st_fd; + + if (fd < 0) + return -EINVAL; + + memset(&st_fd, 0, sizeof(st_fd)); + if (fstat(fd, &st_fd) < 0) + return -errno; + + if (!S_ISFIFO(st_fd.st_mode)) + return 0; + + if (path) { + struct stat st_path; + + memset(&st_path, 0, sizeof(st_path)); + if (fstat(fd, &st_path) < 0) { + + if (errno == ENOENT || errno == ENOTDIR) + return 0; + + return -errno; + } + + return + st_path.st_dev == st_fd.st_dev && + st_path.st_ino == st_fd.st_ino; + } + + return 1; +} + +int sd_is_socket(int fd, int type, int listening) { + struct stat st_fd; + + if (fd < 0 || type < 0) + return -EINVAL; + + if (fstat(fd, &st_fd) < 0) + return -errno; + + if (!S_ISSOCK(st_fd.st_mode)) + return 0; + + if (type != 0) { + int other_type = 0; + socklen_t l = sizeof(other_type); + + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0) + return -errno; + + if (l != sizeof(other_type)) + return -EINVAL; + + if (other_type != type) + return 0; + } + + if (listening >= 0) { + int accepting = 0; + socklen_t l = sizeof(accepting); + + if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) + return -errno; + + if (l != sizeof(accepting)) + return -EINVAL; + + if (!accepting == !listening) + return 0; + } + + return 1; +} + +int sd_is_socket_inet(int fd, int type, int listening, uint16_t port) { + union { + struct sockaddr sa; + struct sockaddr_in in4; + struct sockaddr_in6 in6; + struct sockaddr_un un; + struct sockaddr_storage storage; + } sockaddr; + socklen_t l; + int r; + + if ((r = sd_is_socket(fd, type, listening)) <= 0) + return r; + + memset(&sockaddr, 0, sizeof(sockaddr)); + l = sizeof(sockaddr); + + if (getsockname(fd, &sockaddr.sa, &l) < 0) + return -errno; + + if (l < sizeof(struct sockaddr)) + return -EINVAL; + + if (sockaddr.sa.sa_family != AF_INET && + sockaddr.sa.sa_family != AF_INET6) + return 0; + + if (port > 0) { + if (sockaddr.sa.sa_family == AF_INET) { + if (l < sizeof(struct sockaddr_in)) + return -EINVAL; + + return htons(port) == sockaddr.in4.sin_port; + } else { + if (l < sizeof(struct sockaddr_in6)) + return -EINVAL; + + return htons(port) == sockaddr.in6.sin6_port; + } + } + + return 1; +} + +int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { + union { + struct sockaddr sa; + struct sockaddr_in in4; + struct sockaddr_in6 in6; + struct sockaddr_un un; + struct sockaddr_storage storage; + } sockaddr; + socklen_t l; + int r; + + if ((r = sd_is_socket(fd, type, listening)) <= 0) + return r; + + memset(&sockaddr, 0, sizeof(sockaddr)); + l = sizeof(sockaddr); + + if (getsockname(fd, &sockaddr.sa, &l) < 0) + return -errno; + + if (l < sizeof(sa_family_t)) + return -EINVAL; + + if (sockaddr.sa.sa_family != AF_UNIX) + return 0; + + if (path) { + if (length <= 0) + length = strlen(path); + + if (length <= 0) + /* Unnamed socket */ + return l == sizeof(sa_family_t); + + if (l < sizeof(sa_family_t) + length + 1) + return 0; + + if (path[0]) + /* Normal path socket */ + return memcmp(path, sockaddr.un.sun_path, length+1) == 0; + else + /* Abstract namespace socket */ + return memcmp(path, sockaddr.un.sun_path+1, length) == 0; + } + + return 1; +} diff --git a/src/sd-daemon.h b/src/sd-daemon.h index c7f5c1d6b..b7100bcd5 100644 --- a/src/sd-daemon.h +++ b/src/sd-daemon.h @@ -27,6 +27,8 @@ SOFTWARE. ***/ +#include + /* Reference implementation of a few systemd related interfaces for * writing daemons. These interfaces are trivial to implement, however * to simplify porting we provide this reference @@ -55,7 +57,45 @@ /* Returns how many file descriptors have been passed, or a negative * errno code on failure. Optionally removes the $LISTEN_FDS and - * $LISTEN_PID file descriptors from the environment (recommended). */ + * $LISTEN_PID file descriptors from the environment + * (recommended). You'll find the file descriptors passed as fds + * SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1 if r is the return + * value of this functioin. Returns a negative errno style error code + * on failure. */ int sd_listen_fds(int unset_environment); +/* Helper call for identifying a passed file descriptor. Returns 1 if + * the file descriptor is a FIFO in the file system stored under the + * specified path, 0 otherwise. If path is NULL a path name check will + * not be done and the call only verifies if the file descriptor + * refers to a FIFO. Returns a negative errno style error code on + * failure. */ +int sd_is_fifo(int fd, const char *path); + +/* Helper call for identifying a passed file descriptor. Returns 1 if + * the file descriptor is a socket of the specified type (SOCK_DGRAM, + * SOCK_STREAM, ...), 0 otherwise. If type is 0 a socket type check + * will not be done and the call only verifies if the file descriptor + * refers to a socket. Returns a negative errno style error code on + * failure. */ +int sd_is_socket(int fd, int type, int listening); + +/* Helper call for identifying a passed file descriptor. Returns 1 if + * the file descriptor is an Internet socket (either AF_INET or + * AF_INET6) of the specified type (SOCK_DGRAM, SOCK_STREAM, ...), 0 + * otherwise. If type is 0 a socket type check will not be done. If + * port is 0 a socket port check will not be done. Returns a negative + * errno style error code on failure. */ +int sd_is_socket_inet(int fd, int type, int listening, uint16_t port); + +/* Helper call for identifying a passed file descriptor. Returns 1 if + * the file descriptor is an AF_UNIX socket of the specified type + * (SOCK_DGRAM, SOCK_STREAM, ...) and path, 0 otherwise. If type is 0 + * a socket type check will not be done. If path is NULL a socket path + * check will not be done. For normal AF_UNIX sockets set length to + * 0. For abstract namespace sockets set length to the length of the + * socket name (excluding the initial 0 byte). Returns a negative + * errno style error code on failure. */ +int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length); + #endif