diff options
author | Lennart Poettering <lennart@poettering.net> | 2010-05-20 01:13:43 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2010-05-20 01:13:43 +0200 |
commit | 7c394faa38de389638e19b19212ed50aca870e3c (patch) | |
tree | 475a9da2fa7d10160705ed9a83bc419a75718b90 | |
parent | ff876e283a61320b718ec752d93b1fd40a5fdd0c (diff) |
sd-daemon: add API to verify socket types
-rw-r--r-- | src/initctl.c | 16 | ||||
-rw-r--r-- | src/logger.c | 18 | ||||
-rw-r--r-- | src/sd-daemon.c | 175 | ||||
-rw-r--r-- | src/sd-daemon.h | 42 |
4 files changed, 246 insertions, 5 deletions
diff --git a/src/initctl.c b/src/initctl.c index 9d8eceea52..407d32d93f 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 e7c2be01e0..c486a8acd1 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 cc972dabd7..8a7c9e8dba 100644 --- a/src/sd-daemon.c +++ b/src/sd-daemon.c @@ -24,10 +24,15 @@ SOFTWARE. ***/ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> #include <stdlib.h> #include <errno.h> -#include <sys/types.h> #include <unistd.h> +#include <string.h> #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 c7f5c1d6b8..b7100bcd5e 100644 --- a/src/sd-daemon.h +++ b/src/sd-daemon.h @@ -27,6 +27,8 @@ SOFTWARE. ***/ +#include <inttypes.h> + /* 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 |