diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 216b4046..8f311108 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -41,11 +41,13 @@ set(SOURCES src/sys/mount.cpp src/sys/resource.cpp src/sys/signal.cpp + src/sys/socket.cpp src/fs/VFS.cpp src/fs/Pipe.cpp src/fs/Mount.cpp src/fs/MBR.cpp src/fs/GPT.cpp + src/net/UnixSocket.cpp src/fs/tmpfs/FileSystem.cpp src/fs/tmpfs/Inode.cpp src/fs/ext2/FileSystem.cpp diff --git a/kernel/src/net/Socket.h b/kernel/src/net/Socket.h new file mode 100644 index 00000000..766a7b58 --- /dev/null +++ b/kernel/src/net/Socket.h @@ -0,0 +1,114 @@ +#pragma once +#include "fs/VFS.h" +#include + +class Socket : public VFS::FileInode +{ + public: + Socket() = default; + + VFS::InodeType type() const override + { + return VFS::InodeType::Socket; + } + + void set_fs(VFS::FileSystem* fs) + { + m_fs = fs; + } + + void set_inode_number(usize inum) + { + m_inode_number = inum; + } + + VFS::FileSystem* fs() const override + { + return m_fs; + } + + usize inode_number() const override + { + return m_inode_number; + } + + Result read(u8* buf, usize, usize length) const override + { + return recv(buf, length, 0); + } + + Result write(const u8* buf, usize, usize length) override + { + return send(buf, length, 0); + } + + virtual Result send(const u8*, usize, int) = 0; + + virtual Result recv(u8*, usize, int) const = 0; + + virtual Result bind(SharedPtr, struct sockaddr*, socklen_t) = 0; + virtual Result connect(struct sockaddr*, socklen_t) = 0; + + Result truncate(usize) override + { + return err(EINVAL); + } + + usize size() const override + { + return 0; + } + + mode_t mode() const override + { + return m_mode; + } + + u32 uid() const override + { + return m_uid; + } + + u32 gid() const override + { + return m_gid; + } + + nlink_t nlinks() const override + { + return (nlink_t)m_nlinks; + } + + Result chmod(mode_t mode) override + { + m_mode = mode; + return {}; + } + + Result chown(u32 uid, u32 gid) override + { + m_uid = uid; + m_gid = gid; + return {}; + } + + void did_link() override + { + m_nlinks++; + } + + void did_unlink() override + { + m_nlinks--; + } + + virtual ~Socket() = default; + + protected: + VFS::FileSystem* m_fs; + usize m_inode_number { 0 }; + mode_t m_mode; + u32 m_uid { 0 }; + u32 m_gid { 0 }; + u32 m_nlinks { 0 }; +}; diff --git a/kernel/src/net/UnixSocket.cpp b/kernel/src/net/UnixSocket.cpp new file mode 100644 index 00000000..c5514bd8 --- /dev/null +++ b/kernel/src/net/UnixSocket.cpp @@ -0,0 +1,104 @@ +#include "net/UnixSocket.h" +#include +#include + +UnixSocket::UnixSocket() +{ +} + +UnixSocket::UnixSocket(UnixSocket* peer) : m_state(State::Connected), m_peer(peer) +{ +} + +UnixSocket::~UnixSocket() +{ + if (m_peer) + { + m_peer->m_peer = nullptr; + m_peer->m_state = State::Reset; + } +} + +void UnixSocket::connect_to_peer(UnixSocket* peer) +{ + m_peer = peer; + m_state = State::Connected; +} + +Result UnixSocket::send(const u8* buf, usize length, int) +{ + if (m_state == State::Reset) return err(ECONNRESET); + if (m_state != State::Connected) return err(ENOTCONN); + + check(m_peer); + + TRY(m_peer->m_data.append_data(buf, length)); + + return length; +} + +Result UnixSocket::recv(u8* buf, usize length, int) const +{ + if (m_state == State::Reset) return err(ECONNRESET); + if (m_state != State::Connected) return err(ENOTCONN); + + return m_data.dequeue_data(buf, length); +} + +static Result bind_socket_to_fs(const char* path, Credentials auth, SharedPtr working_directory, + SharedPtr socket) +{ + auto parent_path = TRY(PathParser::dirname(path)); + + auto parent_inode = TRY(VFS::resolve_path(parent_path.chars(), auth, working_directory)); + + if (!VFS::can_write(parent_inode, auth)) return err(EACCES); + + auto child_name = TRY(PathParser::basename(path)); + + TRY(VFS::validate_filename(child_name.view())); + + socket->set_inode_number(TRY(parent_inode->fs()->allocate_inode_number())); + socket->set_fs(parent_inode->fs()); + + return parent_inode->add_entry(socket, child_name.chars()); +} + +Result UnixSocket::bind(SharedPtr socket, struct sockaddr* addr, socklen_t addrlen) +{ + if (!addr) return err(EDESTADDRREQ); + if (addr->sa_family != AF_UNIX) return err(EAFNOSUPPORT); + if ((usize)addrlen > sizeof(sockaddr_un)) return err(EINVAL); + + if (m_state == State::Connected) return err(EISCONN); + if (m_state != State::Inactive) return err(EINVAL); + + struct sockaddr_un* un_address = (struct sockaddr_un*)addr; + + String path = TRY(String::from_string_view( + StringView::from_fixed_size_cstring(un_address->sun_path, addrlen - sizeof(sa_family_t)))); + + auto* current = Scheduler::current(); + + socket->chmod(0777 & ~current->umask); + socket->chown(current->auth.euid, current->auth.egid); + + auto rc = bind_socket_to_fs(path.chars(), current->auth, current->current_directory, socket); + if (rc.has_error()) + { + if (rc.error() == EEXIST) return err(EADDRINUSE); + return rc.release_error(); + } + + memcpy(&m_addr, un_address, addrlen); + m_addrlen = addrlen; + + m_state = State::Bound; + + return {}; +} + +Result UnixSocket::connect(struct sockaddr*, socklen_t) +{ + return err(ENOSYS); +} diff --git a/kernel/src/net/UnixSocket.h b/kernel/src/net/UnixSocket.h new file mode 100644 index 00000000..f03959c1 --- /dev/null +++ b/kernel/src/net/UnixSocket.h @@ -0,0 +1,46 @@ +#pragma once +#include "net/Socket.h" +#include +#include +#include + +class UnixSocket : public Socket +{ + public: + UnixSocket(); + UnixSocket(UnixSocket* peer); + + bool blocking() const override + { + return !m_data.size(); + } + + Result send(const u8*, usize, int) override; + Result recv(u8*, usize, int) const override; + + Result bind(SharedPtr, struct sockaddr*, socklen_t) override; + Result connect(struct sockaddr*, socklen_t) override; + + void connect_to_peer(UnixSocket* peer); + + virtual ~UnixSocket(); + + private: + enum State + { + Inactive, + Bound, + Listening, + Connecting, + Connected, + Reset, + }; + + State m_state = State::Inactive; + UnixSocket* m_peer; + + mutable Buffer m_data; + + struct sockaddr_un m_addr = { .sun_family = AF_UNIX, .sun_path = {} }; + socklen_t m_addrlen = sizeof(sa_family_t); +}; diff --git a/kernel/src/sys/socket.cpp b/kernel/src/sys/socket.cpp new file mode 100644 index 00000000..0a486818 --- /dev/null +++ b/kernel/src/sys/socket.cpp @@ -0,0 +1,49 @@ +#include "net/Socket.h" +#include "memory/MemoryManager.h" +#include "net/UnixSocket.h" +#include "sys/Syscall.h" +#include "thread/Scheduler.h" +#include + +Result sys_socket(Registers*, SyscallArgs args) +{ + int domain = (int)args[0]; + int type = (int)args[1]; + // protocol is not used for now. + + if (type != SOCK_STREAM) return err(EPROTOTYPE); + if (domain != AF_UNIX) return err(EAFNOSUPPORT); + + auto socket = TRY(make_shared()); + + auto* current = Scheduler::current(); + + int fd = TRY(current->allocate_fd(0)); + + current->fd_table[fd] = FileDescriptor { socket, 0, O_RDWR }; + + return fd; +} + +Result sys_bind(Registers*, SyscallArgs args) +{ + int sockfd = (int)args[0]; + struct sockaddr* addr = (struct sockaddr*)args[1]; + socklen_t addrlen = (socklen_t)args[2]; + + struct sockaddr_storage storage; + if ((usize)addrlen > sizeof(storage)) return err(EINVAL); + if (!MemoryManager::copy_from_user(addr, &storage, addrlen)) return err(EFAULT); + + auto* current = Scheduler::current(); + + auto inode = TRY(current->resolve_fd(sockfd))->inode; + + if (inode->type() != VFS::InodeType::Socket) return err(ENOTSOCK); + + auto socket = (SharedPtr)inode; + + TRY(socket->bind(socket, (struct sockaddr*)&storage, addrlen)); + + return 0; +} diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index 19d4c7d2..54c95acf 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -31,6 +31,7 @@ set(SOURCES src/sys/mount.cpp src/sys/pstat.cpp src/sys/resource.cpp + src/sys/socket.cpp ) if(${LUNA_ARCH} STREQUAL "x86_64") diff --git a/libc/include/bits/socket.h b/libc/include/bits/socket.h new file mode 100644 index 00000000..16d18e17 --- /dev/null +++ b/libc/include/bits/socket.h @@ -0,0 +1,30 @@ +/* bits/socket.h: Socket-related types and structures. */ + +#ifndef _BITS_SOCKET_H +#define _BITS_SOCKET_H + +typedef int socklen_t; +typedef unsigned sa_family_t; + +struct sockaddr +{ + sa_family_t sa_family; + char sa_data[4]; +}; + +struct sockaddr_storage +{ + sa_family_t ss_family; + union { + char _sun_path[108]; // AF_UNIX + }; +}; + +#define SOCK_STREAM 0 + +#define AF_UNSPEC 0 +#define AF_UNIX 1 +#define AF_INET 2 +#define AF_INET6 3 + +#endif diff --git a/libc/include/sys/socket.h b/libc/include/sys/socket.h new file mode 100644 index 00000000..5183e974 --- /dev/null +++ b/libc/include/sys/socket.h @@ -0,0 +1,23 @@ +/* sys/socket.h: Communication sockets. */ + +#ifndef _SYS_SOCKET_H +#define _SYS_SOCKET_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Create a new socket and return a file descriptor pointing to it. */ + int socket(int domain, int type, int protocol); + + /* Bind a socket to an address. */ + int bind(int sockfd, struct sockaddr* addr, socklen_t addrlen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/include/sys/un.h b/libc/include/sys/un.h new file mode 100644 index 00000000..7b0dea2f --- /dev/null +++ b/libc/include/sys/un.h @@ -0,0 +1,14 @@ +/* sys/un.h: The sockaddr_un structure for AF_UNIX sockets. */ + +#ifndef _SYS_UN_H +#define _SYS_UN_H + +#include + +struct sockaddr_un +{ + sa_family_t sun_family; + char sun_path[108]; +}; + +#endif diff --git a/libc/src/sys/socket.cpp b/libc/src/sys/socket.cpp new file mode 100644 index 00000000..686d4da7 --- /dev/null +++ b/libc/src/sys/socket.cpp @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +extern "C" +{ + int socket(int domain, int type, int protocol) + { + long rc = syscall(SYS_socket, domain, type, protocol); + __errno_return(rc, int); + } + + int bind(int sockfd, struct sockaddr* addr, socklen_t addrlen) + { + long rc = syscall(SYS_bind, sockfd, addr, addrlen); + __errno_return(rc, int); + } +} diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index b8ea05d5..f82a9c72 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -7,7 +7,7 @@ _e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \ _e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \ _e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \ - _e(getpgid) + _e(getpgid) _e(socket) _e(bind) enum Syscalls {