From 0b92bb76b0005c810ca5cb1a4eb818a2ca4916fe Mon Sep 17 00:00:00 2001 From: TuxSH Date: Tue, 30 Jan 2018 23:59:56 +0100 Subject: [PATCH] Add newlib wrapper for sockets... ... fix several bugs, etc. --- nx/include/switch.h | 3 +- nx/include/switch/ipc.h | 13 +- nx/include/switch/result.h | 1 + nx/include/switch/runtime/devices/socket.h | 29 + nx/include/switch/services/bsd.h | 20 +- nx/include/sys/ioccom.h | 3 +- nx/include/sys/socket.h | 6 +- nx/include/sys/sysctl.h | 3 +- nx/source/runtime/devices/socket.c | 787 +++++++++++++++++++++ nx/source/services/bsd.c | 100 ++- 10 files changed, 884 insertions(+), 81 deletions(-) create mode 100644 nx/include/switch/runtime/devices/socket.h create mode 100644 nx/source/runtime/devices/socket.c diff --git a/nx/include/switch.h b/nx/include/switch.h index 2390d23c..19d3b2eb 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -36,7 +36,7 @@ extern "C" { #include "switch/services/apm.h" #include "switch/services/applet.h" #include "switch/services/audout.h" -#include "switch/services/bsd.h" +//#include "switch/services/bsd.h" Use switch/runtime/devices/socket.h instead #include "switch/services/fatal.h" #include "switch/services/time.h" #include "switch/services/usb.h" @@ -63,6 +63,7 @@ extern "C" { #include "switch/runtime/devices/usb_comms.h" #include "switch/runtime/devices/fs_dev.h" #include "switch/runtime/devices/romfs_dev.h" +#include "switch/runtime/devices/socket.h" #ifdef __cplusplus } diff --git a/nx/include/switch/ipc.h b/nx/include/switch/ipc.h index 69458a32..98aaa9e9 100644 --- a/nx/include/switch/ipc.h +++ b/nx/include/switch/ipc.h @@ -14,8 +14,6 @@ /// IPC output header magic #define SFCO_MAGIC 0x4f434653 -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" - ///@name IPC request building ///@{ @@ -47,16 +45,7 @@ typedef struct { * @param cmd IPC command structure. */ static inline void ipcInitialize(IpcCommand* cmd) { - cmd->NumSend = 0; - cmd->NumRecv = 0; - cmd->NumTransfer = 0; - - cmd->NumStaticIn = 0; - cmd->NumStaticOut = 0; - - cmd->SendPid = false; - cmd->NumHandlesCopy = 0; - cmd->NumHandlesMove = 0; + *cmd = (IpcCommand){0}; } /// IPC buffer descriptor. diff --git a/nx/include/switch/result.h b/nx/include/switch/result.h index 3299aa6e..a2085e18 100644 --- a/nx/include/switch/result.h +++ b/nx/include/switch/result.h @@ -65,6 +65,7 @@ enum { LibnxError_WeirdKernel, LibnxError_IncompatSysVer, LibnxError_InitFail_Time, + LibnxError_TooManyDevOpTabs, }; /// libnx nvidia error codes diff --git a/nx/include/switch/runtime/devices/socket.h b/nx/include/switch/runtime/devices/socket.h new file mode 100644 index 00000000..234fb3d5 --- /dev/null +++ b/nx/include/switch/runtime/devices/socket.h @@ -0,0 +1,29 @@ +#pragma once +#include "../../types.h" + +/// Configuration structure for socketInitalize +typedef struct { + u32 bsdsockets_version; ///< Observed 1 on 2.0 LibAppletWeb, 2 on 3.0. + + u32 tcp_tx_buf_size; ///< Size of the TCP transfer (send) buffer (initial or fixed). + u32 tcp_rx_buf_size; ///< Size of the TCP recieve buffer (initial or fixed). + u32 tcp_tx_buf_max_size; ///< Maximum size of the TCP transfer (send) buffer. If it is 0, the size of the buffer is fixed to its initial value. + u32 tcp_rx_buf_max_size; ///< Maximum size of the TCP receive buffer. If it is 0, the size of the buffer is fixed to its initial value. + + u32 udp_tx_buf_size; ///< Size of the UDP transfer (send) buffer (typically 0x2400 bytes). + u32 udp_rx_buf_size; ///< Size of the UDP receive buffer (typically 0xA500 bytes). + + u32 sb_efficiency; ///< Number of buffers for each socket (standard values range from 1 to 8). +} SocketInitConfig; + +const SocketInitConfig *socketGetDefaultInitConfig(void); +Result socketInitialize(const SocketInitConfig *config); +Result socketGetLastResult(void); + +void socketExit(void); + +static inline Result socketInitializeDefault(void) +{ + return socketInitialize(socketGetDefaultInitConfig()); +} + diff --git a/nx/include/switch/services/bsd.h b/nx/include/switch/services/bsd.h index 2e58200a..ff306166 100644 --- a/nx/include/switch/services/bsd.h +++ b/nx/include/switch/services/bsd.h @@ -27,13 +27,13 @@ typedef struct { u32 udp_rx_buf_size; ///< Size of the UDP receive buffer (typically 0xA500 bytes). u32 sb_efficiency; ///< Number of buffers for each socket (standard values range from 1 to 8). -} BsdBufferConfig; +} BsdInitConfig; -__thread Result t_bsdResult; ///< Last Switch "result", per-thread -__thread int t_bsdErrno; ///< Last errno, per-thread +extern __thread Result g_bsdResult; ///< Last Switch "result", per-thread +extern __thread int g_bsdErrno; ///< Last errno, per-thread -const BsdBufferConfig *bsdGetDefaultBufferConfig(void); -Result bsdInitialize(const BsdBufferConfig *config); +const BsdInitConfig *bsdGetDefaultConfig(void); +Result bsdInitialize(const BsdInitConfig *config); void bsdExit(void); int bsdSocket(int domain, int type, int protocol); @@ -46,16 +46,16 @@ ssize_t bsdRecv(int sockfd, void *buf, size_t len, int flags); ssize_t bsdRecvFrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); ssize_t bsdSend(int sockfd, const void* buf, size_t len, int flags); ssize_t bsdSendTo(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); -int bsdAccept(int sockfd, struct sockaddr *address, socklen_t *addrlen); +int bsdAccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); int bsdBind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int bsdConnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); -int bsdGetPeerName(int sockfd, struct sockaddr *address, socklen_t *addrlen); -int bsdGetSockName(int sockfd, struct sockaddr *address, socklen_t *addrlen); +int bsdGetPeerName(int sockfd, struct sockaddr *addr, socklen_t *addrlen); +int bsdGetSockName(int sockfd, struct sockaddr *addr, socklen_t *addrlen); int bsdGetSockOpt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); int bsdListen(int sockfd, int backlog); // The following two functions are supposed to be variadic int bsdIoctl(int fd, int request, void *data); -int bsdFnctl(int fd, int cmd, int flags); +int bsdFcntl(int fd, int cmd, int flags); int bsdSetSockOpt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); int bsdShutdown(int sockfd, int how); int bsdShutdownAllSockets(int how); @@ -68,5 +68,5 @@ int bsdDuplicateSocket(int sockfd); static inline Result bsdInitializeDefault(void) { - return bsdInitialize(bsdGetDefaultBufferConfig()); + return bsdInitialize(bsdGetDefaultConfig()); } diff --git a/nx/include/sys/ioccom.h b/nx/include/sys/ioccom.h index 2f1bde7f..eaaf8089 100644 --- a/nx/include/sys/ioccom.h +++ b/nx/include/sys/ioccom.h @@ -67,7 +67,8 @@ #include __BEGIN_DECLS -int ioctl(int, unsigned long, ...); +// Modified declaration +int ioctl(int fd, int request, ...); __END_DECLS #endif /* !_SYS_IOCCOM_H_ */ diff --git a/nx/include/sys/socket.h b/nx/include/sys/socket.h index 88e06c1f..ef8c15e4 100644 --- a/nx/include/sys/socket.h +++ b/nx/include/sys/socket.h @@ -544,11 +544,11 @@ ssize_t recv(int sockfd, void *buf, size_t len, int flags); ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); ssize_t send(int sockfd, const void* buf, size_t len, int flags); ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); -int accept(int sockfd, struct sockaddr *address, socklen_t *addrlen); +int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); -int getpeername(int sockfd, struct sockaddr *address, socklen_t *addrlen); -int getsockname(int sockfd, struct sockaddr *address, socklen_t *addrlen); +int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen); +int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen); int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); int listen(int sockfd, int backlog); int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); diff --git a/nx/include/sys/sysctl.h b/nx/include/sys/sysctl.h index 18328cda..1c0b93a1 100644 --- a/nx/include/sys/sysctl.h +++ b/nx/include/sys/sysctl.h @@ -1,4 +1,5 @@ // TuxSH: removed definitions under _KERNEL ifdef blocks, modify the prototype of some functions, some other cleanup, etc. +// Note: I didn't provide #ifndef __BSD_VISIBLE #define __BSD_VISIBLE #endif @@ -311,7 +312,7 @@ struct ctlname { // Modified: added arg names, etc __BEGIN_DECLS -int sysctl(const int *name, u_int namelen, void *oldp, size_t *oldlenp, const void *newp, size_t newlen); +int sysctl(const int *name, unsigned int namelen, void *oldp, size_t *oldlenp, const void *newp, size_t newlen); int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, const void *newp, size_t newlen); int sysctlnametomib(const char *name, int *mibp, size_t *sizep); __END_DECLS diff --git a/nx/source/runtime/devices/socket.c b/nx/source/runtime/devices/socket.c new file mode 100644 index 00000000..5fe50556 --- /dev/null +++ b/nx/source/runtime/devices/socket.c @@ -0,0 +1,787 @@ +#include +#include +#include +//#include +#include +#include +//#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "runtime/devices/socket.h" +#include "services/bsd.h" +#include "result.h" + +static int _socketOpen(struct _reent *r, void *fdptr, const char *path, int flags, int mode); +static int _socketClose(struct _reent *r, void *fdptr); +static ssize_t _socketWrite(struct _reent *r, void *fdptr, const char *buf, size_t count); +static ssize_t _socketRead(struct _reent *r, void *fdptr, char *buf, size_t count); + +static const devoptab_t g_socketDevoptab = { + .name = "soc", + .structSize = sizeof(int), + .open_r = _socketOpen, + .close_r = _socketClose, + .write_r = _socketWrite, + .read_r = _socketRead, + .seek_r = NULL, + .fstat_r = NULL, + .stat_r = NULL, + .link_r = NULL, + .unlink_r = NULL, + .chdir_r = NULL, + .rename_r = NULL, + .mkdir_r = NULL, + .dirStateSize = 0, + .diropen_r = NULL, + .dirreset_r = NULL, + .dirnext_r = NULL, + .dirclose_r = NULL, + .statvfs_r = NULL, + .ftruncate_r = NULL, + .fsync_r = NULL, + .deviceData = 0, + .chmod_r = NULL, + .fchmod_r = NULL, + .rmdir_r = NULL, +}; + +static const SocketInitConfig g_defaultSocketInitConfig = { + .bsdsockets_version = 1, + + .tcp_tx_buf_size = 0x8000, + .tcp_rx_buf_size = 0x10000, + .tcp_tx_buf_max_size = 0x40000, + .tcp_rx_buf_max_size = 0x40000, + + .udp_tx_buf_size = 0x2400, + .udp_rx_buf_size = 0xA500, + + .sb_efficiency = 4, +}; + +static int _socketParseBsdResult(struct _reent *r, int ret) { + int errno_; + if(ret != -1) + return ret; // Nothing to do + else { + if(g_bsdErrno == -1) { + // We're using -1 to signal Switch error codes. + // Note: all of the bsd:u/s handlers return 0. + switch(g_bsdResult) { + case 0xD201: + errno_ = ENFILE; + case 0xD401: + errno_ = EFAULT; + break; + case 0x10601: + errno_ = EBUSY; + break; + default: + errno_ = EPIPE; + break; + } + } + else + errno_ = g_bsdErrno; // Nintendo actually used the Linux errno definitions for their FreeBSD build :) + } + + if(r == NULL) + errno = errno_; + else + r->_errno = errno_; + + return -1; +} + +static int _socketGetFd(int fd) { + __handle *handle = __get_handle(fd); + if(handle == NULL) { + errno = EBADF; + return -1; + } + if(strcmp(devoptab_list[handle->device]->name, "soc") != 0) { + errno = ENOTSOCK; + return -1; + } + return *(int *)handle->fileStruct; +} + +static int _socketOpen(struct _reent *r, void *fdptr, const char *path, int flags, int mode) { + (void)mode; + int ret = _socketParseBsdResult(r, bsdOpen(path, flags)); + if(ret == -1) + return ret; + + *(int *)fdptr = ret; + return 0; +} + +static int _socketClose(struct _reent *r, void *fdptr) { + int fd = *(int *)fdptr; + return _socketParseBsdResult(r, bsdClose(fd)); +} + +static ssize_t _socketWrite(struct _reent *r, void *fdptr, const char *buf, size_t count) { + int fd = *(int *)fdptr; + ssize_t ret = bsdWrite(fd, buf, count); + + _socketParseBsdResult(r, (int)ret); + return ret; +} + +static ssize_t _socketRead(struct _reent *r, void *fdptr, char *buf, size_t count) { + int fd = *(int *)fdptr; + ssize_t ret = bsdRead(fd, buf, count); + + _socketParseBsdResult(r, (int)ret); + return ret; +} + +// Adapted from ctrulib +static int _socketInetAtonDetail(int *outBase, size_t *outNumBytes, const char *cp, struct in_addr *inp) { + int base; + u32 val; + int c; + char bytes[4]; + size_t num_bytes = 0; + + c = *cp; + for(;;) { + if(!isdigit(c)) return 0; + + val = 0; + base = 10; + if(c == '0') { + c = *++cp; + if(c == 'x' || c == 'X') { + base = 16; + c = *++cp; + } + else base = 8; + } + + for(;;) { + if(isdigit(c)) { + if(base == 8 && c >= '8') return 0; + val *= base; + val += c - '0'; + c = *++cp; + } + else if(base == 16 && isxdigit(c)) { + val *= base; + val += c + 10 - (islower(c) ? 'a' : 'A'); + c = *++cp; + } + else break; + } + + if(c == '.') { + if(num_bytes > 3) return 0; + if(val > 0xFF) return 0; + bytes[num_bytes++] = val; + c = *++cp; + } + else break; + } + + if(c != 0) { + *outNumBytes = num_bytes; + *outBase = base; + return 0; + } + + switch(num_bytes) { + case 0: + break; + + case 1: + if(val > 0xFFFFFF) return 0; + val |= bytes[0] << 24; + break; + + case 2: + if(val > 0xFFFF) return 0; + val |= bytes[0] << 24; + val |= bytes[1] << 16; + break; + + case 3: + if(val > 0xFF) return 0; + val |= bytes[0] << 24; + val |= bytes[1] << 16; + val |= bytes[2] << 8; + break; + } + + if(inp) + inp->s_addr = htonl(val); + + *outNumBytes = num_bytes; + *outBase = base; + + return 1; +} + + +const SocketInitConfig *socketGetDefaultInitConfig(void) { + return &g_defaultSocketInitConfig; +} + +Result socketInitialize(const SocketInitConfig *config) { + Result ret = 0; + BsdInitConfig bcfg = { + .version = config->bsdsockets_version, + + .tcp_tx_buf_size = config->tcp_tx_buf_size, + .tcp_rx_buf_size = config->tcp_rx_buf_size, + .tcp_tx_buf_max_size = config->tcp_tx_buf_max_size, + .tcp_rx_buf_max_size = config->tcp_rx_buf_max_size, + + .udp_tx_buf_size = config->udp_tx_buf_size, + .udp_rx_buf_size = config->udp_rx_buf_size, + + .sb_efficiency = config->sb_efficiency, + }; + + int dev = FindDevice("soc:"); + if(dev != -1) + return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized); + + ret = bsdInitialize(&bcfg); + + if(R_SUCCEEDED(ret)) + dev = AddDevice(&g_socketDevoptab); + + if(dev == -1) { + socketExit(); + return MAKERESULT(Module_Libnx, LibnxError_TooManyDevOpTabs); + } + + return ret; +} + +void socketExit(void) { + RemoveDevice("soc:"); + bsdExit(); +} + +Result socketGetLastResult(void) { + return g_bsdResult; +} + +/* + It is way too complicated and inefficient to use devoptab with bsdSelect. + We're therefore implementing select with poll. + + Code copied from libctru. +*/ +int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { + struct pollfd *pollinfo; + nfds_t numfds = 0; + size_t i, j; + int rc, found; + + if(nfds >= FD_SETSIZE || nfds < 0) { + errno = EINVAL; + return -1; + } + + for(i = 0; i < nfds; ++i) { + if((readfds && FD_ISSET(i, readfds)) + || (writefds && FD_ISSET(i, writefds)) + || (exceptfds && FD_ISSET(i, exceptfds))) + ++numfds; + } + + pollinfo = (struct pollfd*)malloc(numfds * sizeof(struct pollfd)); + if(pollinfo == NULL) { + errno = ENOMEM; + return -1; + } + + for(i = 0, j = 0; i < nfds; ++i) { + if((readfds && FD_ISSET(i, readfds)) + || (writefds && FD_ISSET(i, writefds)) + || (exceptfds && FD_ISSET(i, exceptfds))) { + pollinfo[j].fd = i; + pollinfo[j].events = 0; + pollinfo[j].revents = 0; + + if(readfds && FD_ISSET(i, readfds)) + pollinfo[j].events |= POLLIN; + if(writefds && FD_ISSET(i, writefds)) + pollinfo[j].events |= POLLOUT; + + ++j; + } + } + + if(timeout) + rc = poll(pollinfo, numfds, timeout->tv_sec*1000 + timeout->tv_usec/1000); + else + rc = poll(pollinfo, numfds, -1); + + if(rc < 0) { + free(pollinfo); + return rc; + } + + for(i = 0, j = 0, rc = 0; i < nfds; ++i) { + found = 0; + + if((readfds && FD_ISSET(i, readfds)) + || (writefds && FD_ISSET(i, writefds)) + || (exceptfds && FD_ISSET(i, exceptfds))) { + + if(readfds && FD_ISSET(i, readfds)) { + if(pollinfo[j].events & (POLLIN|POLLHUP)) + found = 1; + else + FD_CLR(i, readfds); + } + + if(writefds && FD_ISSET(i, writefds)) { + if(pollinfo[j].events & (POLLOUT|POLLHUP)) + found = 1; + else + FD_CLR(i, writefds); + } + + if(exceptfds && FD_ISSET(i, exceptfds)) { + if(pollinfo[j].events & POLLERR) + found = 1; + else + FD_CLR(i, exceptfds); + } + + if(found) + ++rc; + ++j; + } + } + + free(pollinfo); + + return rc; +} + +// This is much saner than select. +int poll(struct pollfd *fds, nfds_t nfds, int timeout) { + struct pollfd *fds2; + int ret = 0; + + if(fds == NULL) { + errno = EFAULT; + return -1; + } + + fds2 = (struct pollfd *)malloc(nfds * sizeof(struct pollfd)); + if(fds2 == NULL) { + errno = ENOMEM; + return -1; + } + + for(nfds_t i = 0; i < nfds; i++) { + fds2[i].events = fds[i].events; + fds2[i].revents = fds[i].revents; + fds2[i].fd = _socketGetFd(fds[i].fd); + if(fds2[i].fd == -1) { + ret = -1; + break; + } + } + + if(ret != -1) + ret = _socketParseBsdResult(NULL, bsdPoll(fds2, nfds, timeout)); + if(ret != -1) { + for(nfds_t i = 0; i < nfds; i++) { + fds[i].events = fds2[i].events; + fds[i].revents = fds2[i].revents; + } + } + + free(fds2); + return ret; +} + +int sysctl(const int *name, unsigned int namelen, void *oldp, size_t *oldlenp, const void *newp, size_t newlen) { + return _socketParseBsdResult(NULL, bsdSysctl(name, namelen, oldp, oldlenp, newp, newlen)); +} + +int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, const void *newp, size_t newlen) { + int mib[CTL_MAXNAME + 2]; // +2 because most of the relevant code I've seen uses +2 as well + size_t miblen = CTL_MAXNAME + 2; + + if(sysctlnametomib(name, mib, &miblen) == -1) + return -1; + + return sysctl(mib, miblen, oldp, oldlenp, newp, newlen); +} + +int sysctlnametomib(const char *name, int *mibp, size_t *sizep) { + int oid[2] = {0, 3}; // sysctl.name2oid + int ret; + size_t oldlenp; + + if(name == NULL || mibp == NULL || sizep == NULL) { + errno = EFAULT; + return -1; + } + + oldlenp = 4 * (*sizep); + ret = sysctl(oid, 2, mibp, &oldlenp, name, strlen(name)); + *sizep = oldlenp / 4; + + return ret; +} + +int socket(int domain, int type, int protocol) { + int ret, fd, dev; + + dev = FindDevice("soc:"); + if(dev == -1) + return -1; + + fd = __alloc_handle(dev); + if(fd == -1) + return -1; + + ret = _socketParseBsdResult(NULL, bsdSocket(domain, type, protocol)); + if(ret == -1) { + __release_handle(fd); + return -1; + } + else { + *(int *)__get_handle(fd)->fileStruct = ret; + return fd; + } +} + +ssize_t recv(int sockfd, void *buf, size_t len, int flags) { + ssize_t ret; + sockfd = _socketGetFd(sockfd); + if(sockfd == -1) + return -1; + ret = bsdRecv(sockfd, buf, len, flags); + return _socketParseBsdResult(NULL, (int)ret); +} + +ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) { + ssize_t ret; + sockfd = _socketGetFd(sockfd); + if(sockfd == -1) + return -1; + ret = bsdRecvFrom(sockfd, buf, len, flags, src_addr, addrlen); + return _socketParseBsdResult(NULL, (int)ret); +} + +ssize_t send(int sockfd, const void* buf, size_t len, int flags) { + ssize_t ret; + sockfd = _socketGetFd(sockfd); + if(sockfd == -1) + return -1; + ret = bsdSend(sockfd, buf, len, flags); + return _socketParseBsdResult(NULL, (int)ret); +} + +ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { + ssize_t ret; + sockfd = _socketGetFd(sockfd); + if(sockfd == -1) + return -1; + ret = bsdSendTo(sockfd, buf, len, flags, dest_addr, addrlen); + return _socketParseBsdResult(NULL, (int)ret); +} + +int accept(int sockfd, struct sockaddr *address, socklen_t *addrlen) { + int ret, fd, dev; + + fd = _socketGetFd(sockfd); + if(fd == -1) + return -1; + + dev = __get_handle(sockfd)->device; + sockfd = fd; + + fd = __alloc_handle(dev); + if(fd == -1) + return -1; + + ret = _socketParseBsdResult(NULL, bsdAccept(sockfd, address, addrlen)); + if(ret == -1) { + __release_handle(fd); + return -1; + } + else { + *(int *)__get_handle(fd)->fileStruct = ret; + return fd; + } +} + +int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + sockfd = _socketGetFd(sockfd); + if(sockfd == -1) + return -1; + return _socketParseBsdResult(NULL, bsdBind(sockfd, addr, addrlen)); +} + +int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + sockfd = _socketGetFd(sockfd); + if(sockfd == -1) + return -1; + return _socketParseBsdResult(NULL, bsdConnect(sockfd, addr, addrlen)); +} + +int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { + sockfd = _socketGetFd(sockfd); + if(sockfd == -1) + return -1; + return _socketParseBsdResult(NULL, bsdGetPeerName(sockfd, addr, addrlen)); +} + +int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { + sockfd = _socketGetFd(sockfd); + if(sockfd == -1) + return -1; + return _socketParseBsdResult(NULL, bsdGetSockName(sockfd, addr, addrlen)); +} + +int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) { + sockfd = _socketGetFd(sockfd); + if(sockfd == -1) + return -1; + return _socketParseBsdResult(NULL, bsdGetSockOpt(sockfd, level, optname, optval, optlen)); +} + +int listen(int sockfd, int backlog) { + sockfd = _socketGetFd(sockfd); + if(sockfd == -1) + return -1; + return _socketParseBsdResult(NULL, bsdListen(sockfd, backlog)); +} + +int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) { + sockfd = _socketGetFd(sockfd); + if(sockfd == -1) + return -1; + return _socketParseBsdResult(NULL, bsdSetSockOpt(sockfd, level, optname, optval, optlen)); +} + +int ioctl(int fd, int request, ...) { + void *data; + va_list args; + + va_start(args, request); + data = (request & IOC_INOUT) ? va_arg(args, void *) : NULL; + va_end(args); + + if(data == NULL && (request & IOC_INOUT) && IOCPARM_LEN(request) != 0) { + errno = EFAULT; + return -1; + } + + fd = request != FIONBIO ? _socketGetFd(fd) : fd; + if(fd == -1) + return -1; + + switch(request) { + case FIONBIO: { + // See note in fcntl (below) + int flags = fcntl(fd, F_GETFL, 0); + if(flags == -1) + return -1; + flags = *(int *)data != 0 ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK); + return fcntl(fd, F_SETFL, 0); + } + case BIOCSETF: + case BIOCSETWF: + case BIOCSETFNR: { + int ret; + struct bpf_program *prog = (struct bpf_program *)data; + if(prog->bf_len > BPF_MAXBUFSIZE) { + errno = EINVAL; + return -1; + } + + struct bpf_program_serialized *prog_ser = (struct bpf_program_serialized *)malloc(sizeof(struct bpf_program_serialized)); + if(prog_ser == NULL) { + errno = ENOMEM; + return -1; + } + + prog_ser->bf_len = prog->bf_len; + memcpy(prog_ser->bf_insns, prog->bf_insns, prog->bf_len); + + request = _IOC(request & IOC_DIRMASK, IOCGROUP(request), IOCBASECMD(request), sizeof(struct bpf_program_serialized)); + ret = bsdIoctl(fd, request, prog_ser); + free(prog_ser); + return _socketParseBsdResult(NULL, ret); + } + default: + return _socketParseBsdResult(NULL, bsdIoctl(fd, request, data)); + } +} + +int fcntl(int fd, int cmd, ...) { + va_list args; + int flags; + + /* + bsd:u/s only supports F_GETFL and F_SETFL with the O_NONBLOCK flag (or 0). + F_GETFL is implemented using a custom, non-whitelisted IOCTL, whereas + F_SETFL is implemented using FIONBIO. + */ + if(cmd != F_GETFL && cmd != F_SETFL) + return EOPNOTSUPP; + va_start(args, cmd); + flags = va_arg(args, int); + va_end(args); + + fd = _socketGetFd(fd); + if(fd == -1) + return -1; + return _socketParseBsdResult(NULL, bsdFcntl(fd, cmd, flags)); +} + +int shutdown(int sockfd, int how) { + sockfd = _socketGetFd(sockfd); + if(sockfd == -1) + return -1; + return _socketParseBsdResult(NULL, bsdShutdown(sockfd, how)); +} + +int sockatmark(int sockfd) { + int atmark; + return ioctl(sockfd, SIOCATMARK, &atmark) == -1 ? -1 : atmark; +} + +int socketpair(int domain, int type, int protocol, int sv[2]) { + // Unimplementable, function definition written for compliance + errno = ENOSYS; + return -1; +} + +ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) { + if(msg == NULL) { + errno = EFAULT; + return -1; + } + + struct mmsghdr msgvec = { + .msg_hdr = *msg, + .msg_len = 1, + }; + + return recvmmsg(sockfd, &msgvec, 1, flags, NULL); +} + +ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { + if(msg == NULL) { + errno = EFAULT; + return -1; + } + + struct mmsghdr msgvec = { + .msg_hdr = *msg, + .msg_len = 1, + }; + + return sendmmsg(sockfd, &msgvec, 1, flags); +} + +int sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags) { + //TODO: do the necessary RE & implement it + errno = ENOSYS; + return -1; +} +int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout) { + //TODO: do the necessary RE & implement it + errno = ENOSYS; + return -1; +} + +// Adapted from ctrulib +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) { + const u8 *ip = src; + + char *p; + size_t i; + unsigned int n; + + if(af == AF_INET) { + errno = EAFNOSUPPORT; + return NULL; + } + if(size < INET_ADDRSTRLEN) { + errno = ENOSPC; + return NULL; + } + + for(p = dst, i = 0; i < 4; ++i) { + if(i > 0) *p++ = '.'; + + n = ip[i]; + if(n >= 100) { + *p++ = n/100 + '0'; + n %= 100; + } + if(n >= 10 || ip[i] >= 100) { + *p++ = n/10 + '0'; + n %= 10; + } + *p++ = n + '0'; + } + *p = 0; + + return dst; +} + +int inet_pton(int af, const char *src, void *dst) { + int base; + size_t numBytes; + + int ret = _socketInetAtonDetail(&base, &numBytes, src, (struct in_addr *)dst); + return (ret == 1 && base == 10 && numBytes == 4) ? 1 : 0; +} + +char *inet_ntoa(struct in_addr in) { + static char buffer[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &in.s_addr, buffer, INET_ADDRSTRLEN); + return buffer; +} + +int inet_aton(const char *cp, struct in_addr *inp) { + int base; + size_t numBytes; + return _socketInetAtonDetail(&base, &numBytes, cp, inp); +} + +in_addr_t inet_addr(const char *cp) { + struct in_addr addr = { .s_addr = INADDR_BROADCAST }; + inet_aton(cp, &addr); + return addr.s_addr; +} + +long gethostid(void) { + return INADDR_LOOPBACK; //FIXME +} + +int gethostname(char *name, size_t namelen) +{ + //FIXME + long hostid = gethostid(); + const char *hostname = inet_ntop(AF_INET, &hostid, name, namelen); + return hostname == NULL ? -1 : 0; +} diff --git a/nx/source/services/bsd.c b/nx/source/services/bsd.c index febd1f79..460ac3ef 100644 --- a/nx/source/services/bsd.c +++ b/nx/source/services/bsd.c @@ -20,8 +20,8 @@ #include #include -__thread Result t_bsdResult; -__thread int t_bsdErrno; +__thread Result g_bsdResult; +__thread int g_bsdErrno; static Service g_bsdSrv; static Service g_bsdMonitor; @@ -29,7 +29,7 @@ static u64 g_bsdClientPid = -1; static TransferMemory g_bsdTmem; -static const BsdBufferConfig g_defaultBsdBufferConfig = { +static const BsdInitConfig g_defaultBsdInitConfig = { .version = 1, .tcp_tx_buf_size = 0x8000, @@ -48,8 +48,7 @@ static const BsdBufferConfig g_defaultBsdBufferConfig = { Should the transfer memory be smaller than that, the BSD sockets service would only send ZeroWindow packets (for TCP), resulting in a transfer rate not exceeding 1 byte/s. */ -static size_t _bsdGetTransferMemSizeForConfig(const BsdBufferConfig *config) -{ +static size_t _bsdGetTransferMemSizeForConfig(const BsdInitConfig *config) { u32 tcp_tx_buf_max_size = config->tcp_tx_buf_max_size != 0 ? config->tcp_tx_buf_max_size : config->tcp_tx_buf_size; u32 tcp_rx_buf_max_size = config->tcp_rx_buf_max_size != 0 ? config->tcp_rx_buf_max_size : config->tcp_rx_buf_size; u32 sum = tcp_tx_buf_max_size + tcp_rx_buf_max_size + config->udp_tx_buf_size + config->udp_rx_buf_size; @@ -58,7 +57,7 @@ static size_t _bsdGetTransferMemSizeForConfig(const BsdBufferConfig *config) return (size_t)(config->sb_efficiency * sum); } -static Result _bsdRegisterClient(Service* srv, TransferMemory* tmem, const BsdBufferConfig *config, u64* pid_out) { +static Result _bsdRegisterClient(Service* srv, TransferMemory* tmem, const BsdInitConfig *config, u64* pid_out) { IpcCommand c; ipcInitialize(&c); ipcSendPid(&c); @@ -67,7 +66,7 @@ static Result _bsdRegisterClient(Service* srv, TransferMemory* tmem, const BsdBu struct { u64 magic; u64 cmd_id; - BsdBufferConfig config; + BsdInitConfig config; u64 pid_reserved; u64 tmem_sz; } *raw; @@ -93,8 +92,8 @@ static Result _bsdRegisterClient(Service* srv, TransferMemory* tmem, const BsdBu } *resp = r.Raw; *pid_out = resp->pid; - t_bsdResult = rc = resp->result; - t_bsdErrno = 0; + g_bsdResult = rc = resp->result; + g_bsdErrno = 0; } return rc; @@ -128,8 +127,8 @@ static Result _bsdStartMonitor(Service* srv, u64 pid) { u64 result; } *resp = r.Raw; - t_bsdResult = rc = resp->result; - t_bsdErrno = 0; + g_bsdResult = rc = resp->result; + g_bsdErrno = 0; } return rc; @@ -155,26 +154,25 @@ static int _bsdDispatchBasicCommand(IpcCommand *c, IpcParsedCommand *rOut) { rc = resp->result; if (R_SUCCEEDED(rc)) { - t_bsdErrno = resp->errno_; - t_bsdResult = rc = resp->ret; + ret = resp->ret; + g_bsdErrno = (ret < 0) ? resp->errno_ : 0; } } if (R_FAILED(rc)) - t_bsdErrno = -1; + g_bsdErrno = -1; if(rOut != NULL) *rOut = r; + g_bsdResult = rc; return ret; } -static int _bsdDispatchCommandWithOutAddrlen(IpcCommand *c, socklen_t *addrlen) -{ +static int _bsdDispatchCommandWithOutAddrlen(IpcCommand *c, socklen_t *addrlen) { IpcParsedCommand r; int ret = _bsdDispatchBasicCommand(c, &r); - if(ret != -1 && addrlen != NULL) - { + if(ret != -1 && addrlen != NULL) { struct { BsdIpcResponseBase bsd_resp; socklen_t addrlen; @@ -231,11 +229,11 @@ static int _bsdSocketCreationCommand(u32 cmd_id, int domain, int type, int proto return _bsdDispatchBasicCommand(&c, NULL); } -const BsdBufferConfig *bsdGetDefaultBufferConfig(void) { - return &g_defaultBsdBufferConfig; +const BsdInitConfig *bsdGetDefaultInitConfig(void) { + return &g_defaultBsdInitConfig; } -Result bsdInitialize(const BsdBufferConfig *config) { +Result bsdInitialize(const BsdInitConfig *config) { const char* bsd_srv = "bsd:s"; if(serviceIsActive(&g_bsdSrv) || serviceIsActive(&g_bsdMonitor)) @@ -309,19 +307,19 @@ int bsdSelect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, st IpcCommand c; ipcInitialize(&c); - ipcAddSendBuffer(&c, readfds, sizeof(fd_set), 0); - ipcAddSendStatic(&c, readfds, sizeof(fd_set), 0); - ipcAddSendBuffer(&c, writefds, sizeof(fd_set), 0); - ipcAddSendStatic(&c, writefds, sizeof(fd_set), 0); - ipcAddSendBuffer(&c, exceptfds, sizeof(fd_set), 0); - ipcAddSendStatic(&c, exceptfds, sizeof(fd_set), 0); + ipcAddSendBuffer(&c, readfds, readfds == NULL ? 0 : sizeof(fd_set), 0); + ipcAddSendStatic(&c, readfds, readfds == NULL ? 0 : sizeof(fd_set), 0); + ipcAddSendBuffer(&c, writefds, writefds == NULL ? 0 : sizeof(fd_set), 0); + ipcAddSendStatic(&c, writefds, writefds == NULL ? 0 : sizeof(fd_set), 0); + ipcAddSendBuffer(&c, exceptfds, exceptfds == NULL ? 0 : sizeof(fd_set), 0); + ipcAddSendStatic(&c, exceptfds, exceptfds == NULL ? 0 : sizeof(fd_set), 0); - ipcAddRecvBuffer(&c, readfds, sizeof(fd_set), 0); - ipcAddRecvStatic(&c, readfds, sizeof(fd_set), 0); - ipcAddRecvBuffer(&c, writefds, sizeof(fd_set), 0); - ipcAddRecvStatic(&c, writefds, sizeof(fd_set), 0); - ipcAddRecvBuffer(&c, exceptfds, sizeof(fd_set), 0); - ipcAddRecvStatic(&c, exceptfds, sizeof(fd_set), 0); + ipcAddRecvBuffer(&c, readfds, readfds == NULL ? 0 : sizeof(fd_set), 0); + ipcAddRecvStatic(&c, readfds, readfds == NULL ? 0 : sizeof(fd_set), 0); + ipcAddRecvBuffer(&c, writefds, writefds == NULL ? 0 : sizeof(fd_set), 0); + ipcAddRecvStatic(&c, writefds, writefds == NULL ? 0 : sizeof(fd_set), 0); + ipcAddRecvBuffer(&c, exceptfds, exceptfds == NULL ? 0 : sizeof(fd_set), 0); + ipcAddRecvStatic(&c, exceptfds, exceptfds == NULL ? 0 : sizeof(fd_set), 0); struct { u64 magic; @@ -346,11 +344,11 @@ int bsdPoll(struct pollfd *fds, nfds_t nfds, int timeout) { IpcCommand c; ipcInitialize(&c); - ipcAddSendBuffer(&c, fds, sizeof(struct pollfd), 0); - ipcAddSendStatic(&c, fds, sizeof(struct pollfd), 0); + ipcAddSendBuffer(&c, fds, nfds * sizeof(struct pollfd), 0); + ipcAddSendStatic(&c, fds, nfds * sizeof(struct pollfd), 0); - ipcAddRecvBuffer(&c, fds, sizeof(struct pollfd), 0); - ipcAddRecvStatic(&c, fds, sizeof(struct pollfd), 0); + ipcAddRecvBuffer(&c, fds, nfds * sizeof(struct pollfd), 0); + ipcAddRecvStatic(&c, fds, nfds * sizeof(struct pollfd), 0); struct { u64 magic; @@ -395,8 +393,7 @@ int bsdSysctl(const int *name, unsigned int namelen, void *oldp, size_t *oldlenp IpcParsedCommand r; int ret = _bsdDispatchBasicCommand(&c, &r); - if(ret != -1 && oldlenp != NULL) - { + if(ret != -1 && oldlenp != NULL) { struct { BsdIpcResponseBase bsd_resp; size_t oldlenp; @@ -519,12 +516,14 @@ int bsdBind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { struct { u64 magic; u64 cmd_id; + int sockfd; } *raw; raw = ipcPrepareHeader(&c, sizeof(*raw)); raw->magic = SFCI_MAGIC; raw->cmd_id = 13; + raw->sockfd = sockfd; return _bsdDispatchBasicCommand(&c, NULL); } @@ -619,8 +618,7 @@ int bsdIoctl(int fd, int request, void *data) { int bufcount = 1; switch(request) { - case SIOCGIFCONF: - { + case SIOCGIFCONF: { struct ifconf *data_ = (struct ifconf *)data; in1 = out1 = data; in1sz = out1sz = sizeof(struct ifconf); @@ -630,8 +628,7 @@ int bsdIoctl(int fd, int request, void *data) { break; } case SIOCGIFMEDIA: - case SIOCGIFXMEDIA: - { + case SIOCGIFXMEDIA: { struct ifmediareq *data_ = (struct ifmediareq *)data; in1 = out1 = data; in1sz = out1sz = sizeof(struct ifmediareq); @@ -641,18 +638,15 @@ int bsdIoctl(int fd, int request, void *data) { break; } // Generic ioctl - default: - { + default: { void *data_ = NULL; if(request & IOC_INOUT) data_ = data; - if(request & IOC_IN) - { + if(request & IOC_IN) { in1 = data_; in1sz = IOCPARM_LEN(request); } - if(request & IOC_OUT) - { + if(request & IOC_OUT) { out1 = data_; out1sz = IOCPARM_LEN(request); } @@ -699,12 +693,12 @@ int bsdIoctl(int fd, int request, void *data) { return _bsdDispatchBasicCommand(&c, NULL); } -int bsdFnctl(int fd, int cmd, int flags) { +int bsdFcntl(int fd, int cmd, int flags) { IpcCommand c; - if(cmd == F_GETFL || cmd == F_SETFL) { - t_bsdResult = 0; - t_bsdErrno = EINVAL; + if(cmd != F_GETFL && cmd != F_SETFL) { + g_bsdResult = 0; + g_bsdErrno = EOPNOTSUPP; return -1; }