From f876667cc562061bd5c9646aa20302d5be1a35c7 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Mon, 18 Jan 2016 14:43:49 +0100 Subject: [PATCH] Add socketpair(2) AF_UNIX stream support. --- libc/sys/socket/socketpair.cpp | 124 +++++++++++++++++++++++++++++++-- 1 file changed, 119 insertions(+), 5 deletions(-) diff --git a/libc/sys/socket/socketpair.cpp b/libc/sys/socket/socketpair.cpp index 80ca0c4f..73a474fb 100644 --- a/libc/sys/socket/socketpair.cpp +++ b/libc/sys/socket/socketpair.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2013. + Copyright(C) Jonas 'Sortie' Termansen 2016. This file is part of the Sortix C Library. @@ -23,12 +23,126 @@ *******************************************************************************/ #include +#include #include -#include +#include +#include +#include +#include +#include -extern "C" int socketpair(int, int, int, int[2]) +#define FLAGS_MASK (SOCK_NONBLOCK | SOCK_CLOEXEC | SOCK_CLOFORK) +#define TYPE_MASK (~FLAGS_MASK) +#define FLAGS(x) ((x) & FLAGS_MASK) +#define TYPE(x) ((x) & TYPE_MASK) + +static char randchar(void) { - fprintf(stderr, "%s is not implemented yet.\n", __func__); - return errno = ENOSYS, -1; + int index = arc4random_uniform(26 + 26 + 10); + if ( index < 26 ) + return 'a' + index; + if ( index < 26 + 26 ) + return 'A' + (index - 26); + return '0' + (index - (26 + 26)); +} + +struct accept_thread +{ + int listen_fd; + int fd; + int flags; + int errnum; +}; + +static void* accept_thread(void* arg_ptr) +{ + struct accept_thread* arg = (struct accept_thread*) arg_ptr; + arg->fd = accept4(arg->listen_fd, NULL, NULL, arg->flags); + arg->errnum = errno; + return NULL; +} + +static int socketpair_unix_stream(int flags, int fds[2]) +{ + char templ[] = "/tmp/socketpair.XXXXXXXXXX"; + size_t templ_len = strlen(templ); + while ( true ) + { + for ( size_t i = 0; i < 10; i++ ) + templ[templ_len - i] = randchar(); + int listen_flags = SOCK_CLOEXEC | SOCK_CLOFORK; + int listen_fd = socket(AF_UNIX, SOCK_STREAM | listen_flags, 0); + if ( listen_fd < 0 ) + return -1; + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, templ, templ_len + 1); + if ( bind(listen_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0 ) + return -1; + if ( listen(listen_fd, 1) < 0 ) + { + if ( errno == EADDRINUSE ) + continue; + close(listen_fd); + return -1; + } + int client_flags = flags & ~SOCK_NONBLOCK; + int client_fd = socket(AF_UNIX, SOCK_STREAM | client_flags, 0); + if ( client_fd < 0 ) + { + int errnum = errno; + close(listen_fd); + unlink(templ); + return errno = errnum, -1; + } + struct accept_thread arg; + arg.listen_fd = listen_fd; + arg.flags = flags; + pthread_attr_t thread_attr; + pthread_attr_init(&thread_attr); + pthread_attr_setstacksize(&thread_attr, 8192); + pthread_t thread; + int ret = pthread_create(&thread, &thread_attr, accept_thread, &arg); + pthread_attr_destroy(&thread_attr); + if ( ret != 0 ) + { + close(listen_fd); + close(client_fd); + unlink(templ); + return errno = ret, -1; + } + ret = connect(client_fd, (struct sockaddr*) &addr, sizeof(addr)); + pthread_join(thread, NULL); + if ( ret < 0 ) + { + int errnum = errno; + close(listen_fd); + close(client_fd); + unlink(templ); + return errno = errnum, -1; + } + close(listen_fd); + unlink(templ); + if ( flags & SOCK_NONBLOCK ) + fcntl(client_fd, F_SETFL, flags); + fds[0] = client_fd; + fds[1] = arg.fd; + return 0; + } +} + +extern "C" int socketpair(int family, int type, int protocol, int fds[2]) +{ + if ( family == AF_UNIX ) + { + if ( protocol != 0 ) + return errno = EPROTONOSUPPORT, -1; + if ( TYPE(type) == SOCK_STREAM ) + return socketpair_unix_stream(FLAGS(type), fds); + else + return errno = EPROTOTYPE, -1; + } + return errno = EAFNOSUPPORT, -1; }