summaryrefslogtreecommitdiff
path: root/src/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util.c')
-rw-r--r--src/util.c133
1 files changed, 130 insertions, 3 deletions
diff --git a/src/util.c b/src/util.c
index 46e6842..2ebeb4e 100644
--- a/src/util.c
+++ b/src/util.c
@@ -15,11 +15,15 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
#include <ctype.h> /* for isdigit */
-#include <stdlib.h>
-#include <error.h>
#include <errno.h>
+#include <error.h>
+#include <netdb.h> /* for {get,free}addrinfo() */
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/stat.h> /* for open */
+#include <sys/un.h>
+#include <unistd.h> /* for unlink */
#include "util.h"
@@ -86,3 +90,126 @@ int get_fd(const char *addr) {
}
return sock;
}
+
+FILE *xfopen(const char *path, const char *mode) {
+ FILE *stream = fopen(path, mode);
+ if (stream == NULL)
+ error(EXIT_FAILURE, errno, "fopen: %s", path);
+ return stream;
+}
+
+FILE *xfdopen(const char *path, const char *mode) {
+ int fd = get_fd(path);
+ if (fd < 0)
+ error(EXIT_FAILURE, -fd, "get_fd: %s", path);
+ FILE *stream = fdopen(fd, mode);
+ if (stream == NULL)
+ error(EXIT_FAILURE, errno, "fdopen: %s (%d)", path, fd);
+ return stream;
+}
+
+/* same error codes values as -getaddrinfo(); */
+int sockstream_listen(const char *type, const char *addr) {
+ int sock;
+ if (strcmp(type, "fd") == 0) {
+ sock = get_fd(addr);
+ if (sock < 0) {
+ errno = -sock;
+ return -EAI_SYSTEM;
+ }
+ /* make sure it's a socket */
+ struct stat st;
+ if (fstat(sock, &st) < 0)
+ return -EAI_SYSTEM;
+ if (!S_ISSOCK(st.st_mode)) {
+ errno = ENOTSOCK;
+ return -EAI_SYSTEM;
+ }
+ /* make sure the socket is a stream */
+ int fd_socktype = 0;
+ socklen_t l = sizeof(fd_socktype);
+ if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &fd_socktype, &l) < 0)
+ return -EAI_SYSTEM;
+ if (l != sizeof(fd_socktype)) {
+ errno = EINVAL;
+ return -EAI_SYSTEM;
+ }
+ if (fd_socktype != SOCK_STREAM) {
+ errno = ENOSTR;
+ return -EAI_SYSTEM;
+ }
+ /* make sure the socket is listening */
+ int fd_listening = 0;
+ l = sizeof(fd_listening);
+ if (getsockopt(sock, SOL_SOCKET, SO_ACCEPTCONN, &fd_listening, &l) < 0)
+ return -EAI_SYSTEM;
+ if (l != sizeof(fd_listening)) {
+ errno = EINVAL;
+ return -EAI_SYSTEM;
+ }
+ if (!fd_listening)
+ listen(sock, SOMAXCONN);
+ /* return the socket */
+ return sock;
+ } else if (strcmp(type, "unix") == 0) {
+ struct sockaddr_un un_addr = { 0 };
+ if (strlen(addr)+1 > sizeof(un_addr.sun_path)) {
+ errno = ENAMETOOLONG;
+ return -EAI_SYSTEM;
+ }
+ un_addr.sun_family = AF_UNIX;
+ strcpy(un_addr.sun_path, addr);
+ int sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ return -EAI_SYSTEM;
+ unlink(addr);
+ if (bind(sock, (struct sockaddr *)&un_addr, sizeof(un_addr)) < 0)
+ return -EAI_SYSTEM;
+ if (listen(sock, SOMAXCONN) < 0)
+ return -EAI_SYSTEM;
+ return sock;
+ } else if (strcmp(type, "tcp4") == 0 || strcmp(type, "tcp6") == 0) {
+ struct addrinfo *ai_addr = NULL;
+ struct addrinfo ai_hints = { 0 };
+
+ char *host = strdupa(addr);
+ char *col = strrchr(host, ':');
+ if (col == NULL) {
+ errno = EINVAL;
+ return -EAI_SYSTEM;
+ }
+ char *port = &col[1];
+ *col = '\0';
+ if (host[0] == '\0')
+ host = NULL;
+
+ ai_hints.ai_family = (strcmp(type, "tcp6") == 0) ? AF_INET6 : AF_INET;
+ ai_hints.ai_socktype = SOCK_STREAM;
+ ai_hints.ai_flags = AI_PASSIVE;
+
+ int r = getaddrinfo(host, port, &ai_hints, &ai_addr);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return -r;
+
+ int sock = socket(ai_addr->ai_family, ai_addr->ai_socktype, ai_addr->ai_protocol);
+ if (sock < 0)
+ return -EAI_SYSTEM;
+
+ int yes = 1;
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
+
+ if (bind(sock, ai_addr->ai_addr, ai_addr->ai_addrlen) < 0)
+ return -EAI_SYSTEM;
+
+ if (listen(sock, SOMAXCONN) < 0)
+ return -EAI_SYSTEM;
+
+ freeaddrinfo(ai_addr);
+ return sock;
+ } else {
+ errno = EINVAL;
+ return -EAI_SYSTEM;
+ }
+}