From ef2e478607fd9ae56abf1a94f042f1e746be499f Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Thu, 23 Feb 2017 20:12:26 +0100 Subject: [PATCH] Implement getpeername(2) and getsockname(2). --- kernel/descriptor.cpp | 10 ++ kernel/fs/user.cpp | 14 ++ kernel/include/sortix/kernel/descriptor.h | 2 + kernel/include/sortix/kernel/inode.h | 4 + kernel/include/sortix/kernel/syscall.h | 4 +- kernel/include/sortix/kernel/vnode.h | 2 + kernel/inode.cpp | 12 ++ kernel/io.cpp | 22 +-- kernel/net/fs.cpp | 46 +++++++ kernel/vnode.cpp | 10 ++ regress/Makefile | 1 + regress/test-unix-socket-name.c | 158 ++++++++++++++++++++++ 12 files changed, 273 insertions(+), 12 deletions(-) create mode 100644 regress/test-unix-socket-name.c diff --git a/kernel/descriptor.cpp b/kernel/descriptor.cpp index d4ebd17f..2a7055a0 100644 --- a/kernel/descriptor.cpp +++ b/kernel/descriptor.cpp @@ -1057,4 +1057,14 @@ int Descriptor::shutdown(ioctx_t* ctx, int how) return vnode->shutdown(ctx, how); } +int Descriptor::getpeername(ioctx_t* ctx, uint8_t* addr, size_t* addrsize) +{ + return vnode->getpeername(ctx, addr, addrsize); +} + +int Descriptor::getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize) +{ + return vnode->getsockname(ctx, addr, addrsize); +} + } // namespace Sortix diff --git a/kernel/fs/user.cpp b/kernel/fs/user.cpp index 5963517f..c6d3af16 100644 --- a/kernel/fs/user.cpp +++ b/kernel/fs/user.cpp @@ -267,6 +267,8 @@ public: virtual int tcsendbreak(ioctx_t* ctx, int duration); virtual int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio); virtual int shutdown(ioctx_t* ctx, int how); + virtual int getpeername(ioctx_t* ctx, uint8_t* addr, size_t* addrsize); + virtual int getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize); private: bool SendMessage(Channel* channel, size_t type, void* ptr, size_t size, @@ -1748,6 +1750,18 @@ int Unode::shutdown(ioctx_t* ctx, int how) return ret; } +int Unode::getpeername(ioctx_t* /*ctx*/, uint8_t* /*addr*/, + size_t* /*addrsize*/) +{ + return errno = ENOTSOCK, -1; +} + +int Unode::getsockname(ioctx_t* /*ctx*/, uint8_t* /*addr*/, + size_t* /*addrsize*/) +{ + return errno = ENOTSOCK, -1; +} + bool Bootstrap(Ref* out_root, Ref* out_server, const struct stat* rootst) diff --git a/kernel/include/sortix/kernel/descriptor.h b/kernel/include/sortix/kernel/descriptor.h index b9f2bdb3..2feebda9 100644 --- a/kernel/include/sortix/kernel/descriptor.h +++ b/kernel/include/sortix/kernel/descriptor.h @@ -121,6 +121,8 @@ public: int tcsendbreak(ioctx_t* ctx, int duration); int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio); int shutdown(ioctx_t* ctx, int how); + int getpeername(ioctx_t* ctx, uint8_t* addr, size_t* addrsize); + int getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize); private: Ref open_elem(ioctx_t* ctx, const char* filename, int flags, diff --git a/kernel/include/sortix/kernel/inode.h b/kernel/include/sortix/kernel/inode.h index c0f80be0..250f9090 100644 --- a/kernel/include/sortix/kernel/inode.h +++ b/kernel/include/sortix/kernel/inode.h @@ -130,6 +130,8 @@ public: virtual int tcsendbreak(ioctx_t* ctx, int duration) = 0; virtual int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio) = 0; virtual int shutdown(ioctx_t* ctx, int how) = 0; + virtual int getpeername(ioctx_t* ctx, uint8_t* addr, size_t* addrsize) = 0; + virtual int getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize) = 0; }; @@ -235,6 +237,8 @@ public: virtual int tcsendbreak(ioctx_t* ctx, int duration); virtual int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio); virtual int shutdown(ioctx_t* ctx, int how); + virtual int getpeername(ioctx_t* ctx, uint8_t* addr, size_t* addrsize); + virtual int getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize); }; diff --git a/kernel/include/sortix/kernel/syscall.h b/kernel/include/sortix/kernel/syscall.h index 30ffd92a..5d710f8c 100644 --- a/kernel/include/sortix/kernel/syscall.h +++ b/kernel/include/sortix/kernel/syscall.h @@ -95,13 +95,13 @@ uid_t sys_geteuid(void); gid_t sys_getgid(void); int sys_gethostname(char*, size_t); size_t sys_getpagesize(void); -int sys_getpeername(int, struct sockaddr*, socklen_t*); +int sys_getpeername(int, void*, size_t*); pid_t sys_getpgid(pid_t); pid_t sys_getpid(void); pid_t sys_getppid(void); int sys_getpriority(int, id_t); pid_t sys_getsid(pid_t); -int sys_getsockname(int, struct sockaddr*, socklen_t*); +int sys_getsockname(int, void*, size_t*); int sys_getsockopt(int, int, int, void*, size_t*); int sys_gettermmode(int, unsigned*); uid_t sys_getuid(void); diff --git a/kernel/include/sortix/kernel/vnode.h b/kernel/include/sortix/kernel/vnode.h index da768798..0940ffad 100644 --- a/kernel/include/sortix/kernel/vnode.h +++ b/kernel/include/sortix/kernel/vnode.h @@ -118,6 +118,8 @@ public: int tcsendbreak(ioctx_t* ctx, int duration); int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio); int shutdown(ioctx_t* ctx, int how); + int getpeername(ioctx_t* ctx, uint8_t* addr, size_t* addrsize); + int getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize); public /*TODO: private*/: Ref inode; diff --git a/kernel/inode.cpp b/kernel/inode.cpp index 93fde2c7..1dacb5e8 100644 --- a/kernel/inode.cpp +++ b/kernel/inode.cpp @@ -626,4 +626,16 @@ int AbstractInode::shutdown(ioctx_t* /*ctx*/, int /*how*/) return errno = ENOTSOCK, -1; } +int AbstractInode::getpeername(ioctx_t* /*ctx*/, uint8_t* /*addr*/, + size_t* /*addrsize*/) +{ + return errno = ENOTSOCK, -1; +} + +int AbstractInode::getsockname(ioctx_t* /*ctx*/, uint8_t* /*addr*/, + size_t* /*addrsize*/) +{ + return errno = ENOTSOCK, -1; +} + } // namespace Sortix diff --git a/kernel/io.cpp b/kernel/io.cpp index 9ddc4006..9a1e305a 100644 --- a/kernel/io.cpp +++ b/kernel/io.cpp @@ -928,20 +928,22 @@ ssize_t sys_tcsetblob(int fd, const char* name, const void* buffer, size_t count return result; } -int sys_getpeername(int fd, struct sockaddr* addr, socklen_t* addrsize) +int sys_getpeername(int fd, void* addr, size_t* addrsize) { - (void) fd; - (void) addr; - (void) addrsize; - return errno = ENOSYS, -1; + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + return desc->getpeername(&ctx, (uint8_t*) addr, addrsize); } -int sys_getsockname(int fd, struct sockaddr* addr, socklen_t* addrsize) +int sys_getsockname(int fd, void* addr, size_t* addrsize) { - (void) fd; - (void) addr; - (void) addrsize; - return errno = ENOSYS, -1; + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + return desc->getsockname(&ctx, (uint8_t*) addr, addrsize); } int sys_shutdown(int fd, int how) diff --git a/kernel/net/fs.cpp b/kernel/net/fs.cpp index 6d0c29f3..2e913585 100644 --- a/kernel/net/fs.cpp +++ b/kernel/net/fs.cpp @@ -103,6 +103,8 @@ public: virtual int setsockopt(ioctx_t* ctx, int level, int option_name, const void* option_value, size_t option_size); virtual int shutdown(ioctx_t* ctx, int how); + virtual int getpeername(ioctx_t* ctx, uint8_t* addr, size_t* addrsize); + virtual int getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize); private: int do_bind(ioctx_t* ctx, const uint8_t* addr, size_t addrsize); @@ -118,6 +120,7 @@ public: /* For use by Manager. */ StreamSocket* last_pending; struct sockaddr_un* bound_address; size_t bound_address_size; + int shutdown_flags; bool is_listening; bool is_connected; bool is_refused; @@ -170,6 +173,7 @@ StreamSocket::StreamSocket(uid_t owner, gid_t group, mode_t mode, this->last_pending = NULL; this->bound_address = NULL; this->bound_address_size = 0; + this->shutdown_flags = 0; this->is_listening = false; this->is_connected = false; this->is_refused = false; @@ -386,6 +390,41 @@ int StreamSocket::shutdown(ioctx_t* /*ctx*/, int how) incoming.Disconnect(); if ( how & SHUT_WR ) outgoing.Disconnect(); + shutdown_flags |= how; + return 0; +} + +int StreamSocket::getpeername(ioctx_t* ctx, uint8_t* addr, size_t* addrsize) +{ + ScopedLock lock(&socket_lock); + if ( !is_connected ) + return errno = ENOTCONN, -1; + if ( shutdown_flags & SHUT_WR ) + return errno = EINVAL, -1; + size_t used_addrsize; + if ( !ctx->copy_from_src(&used_addrsize, addrsize, sizeof(used_addrsize)) ) + return -1; + if ( bound_address_size < used_addrsize ) + used_addrsize = bound_address_size; + if ( !ctx->copy_to_dest(addr, bound_address, bound_address_size) ) + return -1; + if ( !ctx->copy_to_dest(addrsize, &used_addrsize, sizeof(used_addrsize)) ) + return -1; + return 0; +} + +int StreamSocket::getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize) +{ + ScopedLock lock(&socket_lock); + size_t used_addrsize; + if ( !ctx->copy_from_src(&used_addrsize, addrsize, sizeof(used_addrsize)) ) + return -1; + if ( bound_address_size < used_addrsize ) + used_addrsize = bound_address_size; + if ( !ctx->copy_to_dest(addr, bound_address, bound_address_size) ) + return -1; + if ( !ctx->copy_to_dest(addrsize, &used_addrsize, sizeof(used_addrsize)) ) + return -1; return 0; } @@ -496,6 +535,13 @@ Ref Manager::Accept(StreamSocket* socket, ioctx_t* ctx, if ( !server ) return Ref(NULL); + server->bound_address = (struct sockaddr_un*) malloc(bound_address_size); + if ( !server->bound_address ) + return Ref(NULL); + + server->bound_address_size = bound_address_size; + memcpy(server->bound_address, bound_address, bound_address_size); + StreamSocket* client = socket->first_pending; QueuePop(&socket->first_pending, &socket->last_pending); diff --git a/kernel/vnode.cpp b/kernel/vnode.cpp index 2a59fcc1..27ee4586 100644 --- a/kernel/vnode.cpp +++ b/kernel/vnode.cpp @@ -498,4 +498,14 @@ int Vnode::shutdown(ioctx_t* ctx, int how) return inode->shutdown(ctx, how); } +int Vnode::getpeername(ioctx_t* ctx, uint8_t* addr, size_t* addrsize) +{ + return inode->getpeername(ctx, addr, addrsize); +} + +int Vnode::getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize) +{ + return inode->getsockname(ctx, addr, addrsize); +} + } // namespace Sortix diff --git a/regress/Makefile b/regress/Makefile index 31a42806..c9407b2a 100644 --- a/regress/Makefile +++ b/regress/Makefile @@ -25,6 +25,7 @@ test-pthread-once \ test-pthread-self \ test-pthread-tls \ test-signal-raise \ +test-unix-socket-name \ test-unix-socket-shutdown \ all: $(BINARIES) $(TESTS) diff --git a/regress/test-unix-socket-name.c b/regress/test-unix-socket-name.c new file mode 100644 index 00000000..807a71df --- /dev/null +++ b/regress/test-unix-socket-name.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2017 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * test-unix-socket-name.c + * Tests whether unix sockets return the right names. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "test.h" + +static pid_t main_pid; +static char tmpdir[] = "/tmp/test-unix-socket-name.XXXXXX"; +static char sockpath[(sizeof(tmpdir) - 1) + 1 + 6 + 1]; +static bool made_tmpdir = false; +static bool made_socket = false; + +void exit_handler(void) +{ + if ( getpid() != main_pid ) + return; + if ( made_socket ) + unlink(sockpath); + if ( made_tmpdir ) + rmdir(tmpdir); +} + +int main(void) +{ + main_pid = getpid(); + test_assert(atexit(exit_handler) == 0); + test_assert(mkdtemp(tmpdir)); + made_tmpdir = true; + + int server; + test_assert(0 <= (server = socket(AF_UNIX, SOCK_STREAM, 0))); + + test_assert(snprintf(sockpath, sizeof(sockpath), "%s/socket", tmpdir) < + (int) sizeof(sockpath)); + socklen_t sockaddr_actual_len = + offsetof(struct sockaddr_un, sun_path) + strlen(sockpath) + 1; + struct sockaddr_un sockaddr; + test_assert(sockaddr_actual_len <= sizeof(sockaddr)); + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sun_family = AF_UNIX; + strlcpy(sockaddr.sun_path, sockpath, sizeof(sockaddr.sun_path)); + socklen_t sockaddr_len = sizeof(sockaddr); + + test_assert(bind(server, (struct sockaddr*) &sockaddr, sockaddr_len) == 0); + test_assert(listen(server, 1) == 0 ); + made_socket = true; + + struct sockaddr_un addr; + socklen_t addr_len; + + memset(&addr, 0, sizeof(addr)); + addr_len = sizeof(addr); + test_assert(getsockname(server, (struct sockaddr*) &addr, + &addr_len) == 0); + test_assert(sockaddr_actual_len <= addr_len); + test_assert(addr_len <= sizeof(addr)); + test_assert(addr.sun_family == AF_UNIX); + test_assert(strcmp(sockpath, addr.sun_path) == 0); + + test_assert(getpeername(server, (struct sockaddr*) &addr, + &addr_len) == -1); + test_assert(errno == ENOTCONN); + + memset(&addr, 0, sizeof(addr)); + addr_len = sizeof(addr); + + int client; + pid_t pid; + test_assert(0 <= (pid = fork())); + if ( pid == 0 ) + { + test_assert(0 <= (client = socket(AF_UNIX, SOCK_STREAM, 0))); + test_assert(connect(client, (struct sockaddr*) &sockaddr, + sockaddr_len) == 0); + +#if defined(__sortix__) // Linux returns an empty struct sockaddr_un. + memset(&addr, 0, sizeof(addr)); + addr_len = sizeof(addr); + test_assert(getsockname(client, (struct sockaddr*) &addr, + &addr_len) == 0); + test_assert(sockaddr_actual_len <= addr_len); + test_assert(addr_len <= sizeof(addr)); + test_assert(addr.sun_family == AF_UNIX); + test_assert(strcmp(sockpath, addr.sun_path) == 0); +#endif + + memset(&addr, 0, sizeof(addr)); + addr_len = sizeof(addr); + test_assert(getpeername(client, (struct sockaddr*) &addr, + &addr_len) == 0); + test_assert(sockaddr_actual_len <= addr_len); + test_assert(addr_len <= sizeof(addr)); + test_assert(addr.sun_family == AF_UNIX); + test_assert(strcmp(sockpath, addr.sun_path) == 0); + + _exit(0); + } + + memset(&addr, 0, sizeof(addr)); + addr_len = sizeof(addr); + test_assert(0 <= (client = accept(server, (struct sockaddr*) &addr, + &addr_len))); + test_assert(sockaddr_actual_len <= addr_len); + test_assert(addr_len <= sizeof(addr)); + test_assert(addr.sun_family == AF_UNIX); + test_assert(strcmp(sockpath, addr.sun_path) == 0); + + memset(&addr, 0, sizeof(addr)); + addr_len = sizeof(addr); + test_assert(getsockname(client, (struct sockaddr*) &addr, + &addr_len) == 0); + test_assert(sockaddr_actual_len <= addr_len); + test_assert(addr_len <= sizeof(addr)); + test_assert(addr.sun_family == AF_UNIX); + test_assert(strcmp(sockpath, addr.sun_path) == 0); + +#if defined(__sortix__) // Linux returns an empty struct sockaddr_un. + memset(&addr, 0, sizeof(addr)); + addr_len = sizeof(addr); + test_assert(getpeername(client, (struct sockaddr*) &addr, + &addr_len) == 0); + test_assert(sockaddr_actual_len <= addr_len); + test_assert(addr_len <= sizeof(addr)); + test_assert(addr.sun_family == AF_UNIX); + test_assert(strcmp(sockpath, addr.sun_path) == 0); +#endif + + int status = 0; + waitpid(pid, &status, 0); + if ( !WIFEXITED(status) || WEXITSTATUS(status) != 0 ) + return 1; + + return 0; +}