diff --git a/kernel-src/fs/sockfs.c b/kernel-src/fs/sockfs.c index d8d675e2..4424ce90 100644 --- a/kernel-src/fs/sockfs.c +++ b/kernel-src/fs/sockfs.c @@ -26,12 +26,38 @@ int sockfs_close(vnode_t *node, int flags, cred_t *cred) { int sockfs_read(vnode_t *node, void *buffer, size_t size, uintmax_t offset, int flags, size_t *readc, cred_t *cred) { socket_t *socket = SOCKFS_SOCKET_FROM_NODE(node); - return socket->ops->recv(socket, NULL, buffer, size, flags, readc); + sockdesc_t desc = { + .addr = NULL, + .buffer = buffer, + .count = size, + .flags = flags, + .donecount = 0, + .ctrl = NULL, + .ctrllen = 0 + }; + + int e = socket->ops->recv(socket, &desc); + + *readc = desc.donecount; + return e; } int sockfs_write(vnode_t *node, void *buffer, size_t size, uintmax_t offset, int flags, size_t *writec, cred_t *cred) { socket_t *socket = SOCKFS_SOCKET_FROM_NODE(node); - return socket->ops->send(socket, NULL, buffer, size, flags, writec); + sockdesc_t desc = { + .addr = NULL, + .buffer = buffer, + .count = size, + .flags = flags, + .donecount = 0, + .ctrl = NULL, + .ctrllen = 0 + }; + + int e = socket->ops->send(socket, &desc); + + *writec = desc.donecount; + return e; } int sockfs_poll(vnode_t *node, polldata_t *data, int events) { diff --git a/kernel-src/include/kernel/abi.h b/kernel-src/include/kernel/abi.h index b4b29da9..5a718e9a 100644 --- a/kernel-src/include/kernel/abi.h +++ b/kernel-src/include/kernel/abi.h @@ -88,6 +88,7 @@ typedef struct { #define SOCK_DGRAM 2 #define SOCK_RAW 3 +#define MSG_CTRUNC 0x8 #define MSG_PEEK 2 #define MSG_WAITALL 0x100 #define MSG_NOSIGNAL 0x4000 diff --git a/kernel-src/include/kernel/sock.h b/kernel-src/include/kernel/sock.h index 3162d2db..91603497 100644 --- a/kernel-src/include/kernel/sock.h +++ b/kernel-src/include/kernel/sock.h @@ -29,13 +29,33 @@ typedef struct { }; } sockaddr_t; +typedef struct { + size_t length; + int level; + int type; + uint8_t data[]; +} sockctrl_t; + +typedef struct { + sockaddr_t *addr; + void *buffer; + size_t count; + uintmax_t flags; + size_t donecount; + sockctrl_t *ctrl; + size_t ctrllen; + size_t ctrldone; +} sockdesc_t; + #define SOCKET_RECV_FLAGS_PEEK 0x8000000000000000l #define SOCKET_RECV_FLAGS_WAITALL 0x4000000000000000l +#define SOCKET_RECV_FLAGS_CTRLTRUNCATED 0x2000000000000000l #define SOCKET_SEND_FLAGS_NOSIGNAL 0x8000000000000000l + typedef struct socketops_t { int (*bind)(socket_t *socket, sockaddr_t *addr, cred_t *cred); - int (*send)(socket_t *socket, sockaddr_t *addr, void *buffer, size_t count, uintmax_t flags, size_t *sendcount); - int (*recv)(socket_t *socket, sockaddr_t *addr, void *buffer, size_t count, uintmax_t flags, size_t *recvcount); + int (*send)(socket_t *socket, sockdesc_t *desc); + int (*recv)(socket_t *socket, sockdesc_t *desc); int (*poll)(socket_t *socket, polldata_t *data, int events); int (*connect)(socket_t *socket, sockaddr_t *addr, uintmax_t data, cred_t *cred); int (*listen)(socket_t *socket, int backlog); @@ -155,6 +175,28 @@ static inline void sock_freemsghdr(msghdr_t *hdr) { free(hdr->msgctrl); } +#define SOCK_CTRL_NEXT(c) ((sockctrl_t *)((uintptr_t)(c) + (c)->length)) + +static inline size_t sock_countctrl(sockctrl_t *ctrl, size_t len) { + size_t count = 0; + uintmax_t offset = 0; + + while (offset < len) { + if (ctrl->length < sizeof(sockctrl_t)) + break; + + offset += ctrl->length; + + if (offset > len || offset < ctrl->length) + break; + + ++count; + ctrl = SOCK_CTRL_NEXT(ctrl); + } + + return count; +} + void localsock_leavebinding(vnode_t *vnode); int localsock_pair(socket_t **ret1, socket_t **ret2); socket_t *localsock_createsocket(); diff --git a/kernel-src/io/net/tcp.c b/kernel-src/io/net/tcp.c index 7ca76a11..1269fac8 100644 --- a/kernel-src/io/net/tcp.c +++ b/kernel-src/io/net/tcp.c @@ -1239,8 +1239,8 @@ static int internalpoll(tcb_t *tcb, polldata_t* data, int events) { } // TODO buffer up segments instead of instantly sending them -static int tcp_send(socket_t *socket, sockaddr_t *addr, void *buffer, size_t count, uintmax_t flags, size_t *sendcount) { - if (addr) +static int tcp_send(socket_t *socket, sockdesc_t *sockdesc) { + if (sockdesc->addr) return ENOTCONN; tcpsocket_t *tcpsocket = (tcpsocket_t *)socket; @@ -1272,7 +1272,7 @@ static int tcp_send(socket_t *socket, sockaddr_t *addr, void *buffer, size_t cou poll_leave(&polldesc); poll_destroydesc(&polldesc); - if (_cpu()->thread->proc && (flags & SOCKET_SEND_FLAGS_NOSIGNAL) == 0) + if (_cpu()->thread->proc && (sockdesc->flags & SOCKET_SEND_FLAGS_NOSIGNAL) == 0) signal_signalproc(_cpu()->thread->proc, SIGPIPE); error = EPIPE; @@ -1302,11 +1302,11 @@ static int tcp_send(socket_t *socket, sockaddr_t *addr, void *buffer, size_t cou goto leave; } - size_t writesize = min(TCB_RINGBUFFER_SIZE - RINGBUFFER_DATACOUNT(&tcb->transmitbuffer), count); + size_t writesize = min(TCB_RINGBUFFER_SIZE - RINGBUFFER_DATACOUNT(&tcb->transmitbuffer), sockdesc->count); __assert(writesize); - __assert(ringbuffer_write(&tcb->transmitbuffer, buffer, writesize) == writesize); - *sendcount = writesize; + __assert(ringbuffer_write(&tcb->transmitbuffer, sockdesc->buffer, writesize) == writesize); + sockdesc->donecount = writesize; if (tcb->sndnext == tcb->sndunack) error = tcp_transmitnextsegment(tcb); @@ -1317,9 +1317,12 @@ static int tcp_send(socket_t *socket, sockaddr_t *addr, void *buffer, size_t cou return error; } -static int tcp_recv(socket_t *socket, sockaddr_t *addr, void *buffer, size_t count, uintmax_t flags, size_t *recvcount) { +static int tcp_recv(socket_t *socket, sockdesc_t *sockdesc) { tcpsocket_t *tcpsocket = (tcpsocket_t *)socket; + uintmax_t flags = sockdesc->flags; + sockdesc->flags = 0; int error = 0; + MUTEX_ACQUIRE(&socket->mutex, false); tcb_t *tcb = tcpsocket->tcb; if (tcb == NULL) { @@ -1347,7 +1350,7 @@ static int tcp_recv(socket_t *socket, sockaddr_t *addr, void *buffer, size_t cou if (revents) { if ((revents & POLLHUP) == 0 && (flags & SOCKET_RECV_FLAGS_WAITALL) && - (flags & SOCKET_RECV_FLAGS_PEEK) == 0 && RINGBUFFER_DATACOUNT(&tcb->receivebuffer) < count) { + (flags & SOCKET_RECV_FLAGS_PEEK) == 0 && RINGBUFFER_DATACOUNT(&tcb->receivebuffer) < sockdesc->count) { // wait for more data (WAITALL is set) poll_add(&socket->pollheader, &desc.data[0], POLLIN); } else { @@ -1376,16 +1379,16 @@ static int tcp_recv(socket_t *socket, sockaddr_t *addr, void *buffer, size_t cou goto cleanup; } - size_t copycount = min(RINGBUFFER_DATACOUNT(&tcb->receivebuffer), count); + size_t copycount = min(RINGBUFFER_DATACOUNT(&tcb->receivebuffer), sockdesc->count); if (flags & SOCKET_RECV_FLAGS_PEEK) { - __assert(ringbuffer_peek(&tcb->receivebuffer, buffer, 0, copycount) == copycount); + __assert(ringbuffer_peek(&tcb->receivebuffer, sockdesc->buffer, 0, copycount) == copycount); } else { - __assert(ringbuffer_read(&tcb->receivebuffer, buffer, copycount) == copycount); + __assert(ringbuffer_read(&tcb->receivebuffer, sockdesc->buffer, copycount) == copycount); tcb->rcvwindow += copycount; // notify about the window increase tcp_ack(tcb); } - *recvcount = copycount; + sockdesc->donecount = copycount; cleanup: MUTEX_RELEASE(&tcb->mutex); diff --git a/kernel-src/io/net/udp.c b/kernel-src/io/net/udp.c index ba7927ff..6cc0b61d 100644 --- a/kernel-src/io/net/udp.c +++ b/kernel-src/io/net/udp.c @@ -180,17 +180,17 @@ static int internalpoll(socket_t *socket, polldata_t *data, int events) { return revents; } -static int udp_send(socket_t *socket, sockaddr_t *addr, void *buffer, size_t count, uintmax_t flags, size_t *sendcount) { +static int udp_send(socket_t *socket, sockdesc_t *sockdesc) { udpsocket_t *udpsocket = (udpsocket_t *)socket; int e; MUTEX_ACQUIRE(&socket->mutex, false); - if (addr == NULL && socket->state != SOCKET_STATE_CONNECTED) { + if (sockdesc->addr == NULL && socket->state != SOCKET_STATE_CONNECTED) { e = ENOTCONN; goto cleanup; } - if ((addr ? addr->ipv4addr.addr : udpsocket->peeraddress) == IPV4_BROADCAST_ADDRESS && (udpsocket->socket.broadcast == 0 || udpsocket->socket.netdev == NULL)) { + if ((sockdesc->addr ? sockdesc->addr->ipv4addr.addr : udpsocket->peeraddress) == IPV4_BROADCAST_ADDRESS && (udpsocket->socket.broadcast == 0 || udpsocket->socket.netdev == NULL)) { e = EACCES; goto cleanup; } @@ -203,16 +203,20 @@ static int udp_send(socket_t *socket, sockaddr_t *addr, void *buffer, size_t cou socket->state = SOCKET_STATE_BOUND; } - e = udp_sendpacket(buffer, count, addr ? addr->ipv4addr.addr : udpsocket->peeraddress, udpsocket->port, addr ? addr->ipv4addr.port : udpsocket->peerport, udpsocket->socket.netdev); + e = udp_sendpacket(sockdesc->buffer, sockdesc->count, sockdesc->addr ? sockdesc->addr->ipv4addr.addr : udpsocket->peeraddress, + udpsocket->port, sockdesc->addr ? sockdesc->addr->ipv4addr.port : udpsocket->peerport, udpsocket->socket.netdev); - *sendcount = count; + sockdesc->donecount = sockdesc->count; cleanup: MUTEX_RELEASE(&socket->mutex); return e; } -static int udp_recv(socket_t *socket, sockaddr_t *addr, void *buffer, size_t count, uintmax_t flags, size_t *recvcount) { +static int udp_recv(socket_t *socket, sockdesc_t *sockdesc) { udpsocket_t *udpsocket = (udpsocket_t *)socket; + uintmax_t flags = sockdesc->flags; + sockdesc->flags = 0; + int e = 0; MUTEX_ACQUIRE(&socket->mutex, false); @@ -262,25 +266,25 @@ static int udp_recv(socket_t *socket, sockaddr_t *addr, void *buffer, size_t cou dataheader_t header; if (flags & SOCKET_RECV_FLAGS_PEEK) { __assert(ringbuffer_peek(&udpsocket->ringbuffer, &header, 0, sizeof(header)) == sizeof(header)); - copycount = min(header.length, count); + copycount = min(header.length, sockdesc->count); - __assert(ringbuffer_peek(&udpsocket->ringbuffer, &buffer, sizeof(header), copycount) == copycount); + __assert(ringbuffer_peek(&udpsocket->ringbuffer, sockdesc->buffer, sizeof(header), copycount) == copycount); } else { __assert(ringbuffer_read(&udpsocket->ringbuffer, &header, sizeof(header)) == sizeof(header)); - copycount = min(header.length, count); - __assert(ringbuffer_read(&udpsocket->ringbuffer, buffer, copycount) == copycount); + copycount = min(header.length, sockdesc->count); + __assert(ringbuffer_read(&udpsocket->ringbuffer, sockdesc->buffer, copycount) == copycount); // truncate the rest if the buffer wasn't big enough to handle everything - if (header.length > count) + if (header.length > sockdesc->count) ringbuffer_truncate(&udpsocket->ringbuffer, header.length - copycount); } spinlock_release(&udpsocket->ringbufferlock); interrupt_set(intstate); - *recvcount = copycount; - addr->ipv4addr.addr = header.peer; - addr->ipv4addr.port = header.srcport; + sockdesc->donecount = copycount; + sockdesc->addr->ipv4addr.addr = header.peer; + sockdesc->addr->ipv4addr.port = header.srcport; cleanup: MUTEX_RELEASE(&socket->mutex); diff --git a/kernel-src/sys/syscalls/recvmsg.c b/kernel-src/sys/syscalls/recvmsg.c index 9740c3fc..8c722411 100644 --- a/kernel-src/sys/syscalls/recvmsg.c +++ b/kernel-src/sys/syscalls/recvmsg.c @@ -21,6 +21,13 @@ syscallret_t syscall_recvmsg(context_t *, int fd, msghdr_t *umsghdr, int flags) file_t *file = NULL; size_t buffersize = iovec_size(msghdr.iov, msghdr.iovcount); + if (buffersize == 0) { + sock_freemsghdr(&msghdr); + ret.errno = 0; + ret.ret = 0; + return ret; + } + void *buffer = vmm_map(NULL, buffersize, VMM_FLAGS_ALLOCATE, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC, NULL); if (buffer == NULL) { ret.errno = ENOMEM; @@ -40,7 +47,6 @@ syscallret_t syscall_recvmsg(context_t *, int fd, msghdr_t *umsghdr, int flags) sockaddr_t sockaddr; socket_t *socket = SOCKFS_SOCKET_FROM_NODE(file->vnode); - size_t recvcount; uintmax_t recvflags = 0; if (flags & MSG_PEEK) @@ -49,11 +55,24 @@ syscallret_t syscall_recvmsg(context_t *, int fd, msghdr_t *umsghdr, int flags) if (flags & MSG_WAITALL) recvflags |= SOCKET_RECV_FLAGS_WAITALL; - ret.errno = socket->ops->recv(socket, &sockaddr, buffer, buffersize, fileflagstovnodeflags(file->flags) | recvflags, &recvcount); - ret.ret = ret.errno ? -1 : recvcount; + sockdesc_t desc = { + .addr = &sockaddr, + .buffer = buffer, + .count = buffersize, + .flags = fileflagstovnodeflags(file->flags) | recvflags, + .donecount = 0, + .ctrl = msghdr.msgctrl, + .ctrllen = msghdr.ctrllen, + .ctrldone = 0 + }; + + ret.errno = socket->ops->recv(socket, &desc); if (ret.errno) goto cleanup; + msghdr.ctrllen = desc.ctrldone; + msghdr.flags = (desc.flags & SOCKET_RECV_FLAGS_CTRLTRUNCATED) ? MSG_CTRUNC : 0; + uintmax_t iovoffset = 0; for (int i = 0; i < msghdr.iovcount; ++i) { ret.errno = usercopy_touser(msghdr.iov[i].addr, (void *)((uintptr_t)buffer + iovoffset), msghdr.iov[i].len); @@ -68,9 +87,29 @@ syscallret_t syscall_recvmsg(context_t *, int fd, msghdr_t *umsghdr, int flags) if (ret.errno) goto cleanup; - ret.errno = usercopy_touser(msghdr.addr, &abisockaddr, min(sizeof(abisockaddr), msghdr.addrlen)); + void *ptr; + ret.errno = usercopy_fromuser(&ptr, &umsghdr->addr, sizeof(void *)); + if (ret.errno) + goto cleanup; + + ret.errno = usercopy_touser(ptr, &abisockaddr, min(sizeof(abisockaddr), msghdr.addrlen)); + if (ret.errno) + goto cleanup; } + if (msghdr.msgctrl) { + void *ptr; + ret.errno = usercopy_fromuser(&ptr, &umsghdr->msgctrl, sizeof(void *)); + if (ret.errno) + goto cleanup; + + ret.errno = usercopy_touser(ptr, msghdr.msgctrl, desc.ctrldone); + if (ret.errno) + goto cleanup; + } + + ret.ret = ret.errno ? -1 : desc.donecount; + cleanup: if (file) fd_release(file); diff --git a/kernel-src/sys/syscalls/sendmsg.c b/kernel-src/sys/syscalls/sendmsg.c index 122ee3dc..256d1bd9 100644 --- a/kernel-src/sys/syscalls/sendmsg.c +++ b/kernel-src/sys/syscalls/sendmsg.c @@ -16,6 +16,13 @@ syscallret_t syscall_sendmsg(context_t *, int fd, msghdr_t *umsghdr, int flags) file_t *file = NULL; size_t buffersize = iovec_size(msghdr.iov, msghdr.iovcount); + if (buffersize == 0) { + sock_freemsghdr(&msghdr); + ret.errno = 0; + ret.ret = 0; + return ret; + } + void *buffer = vmm_map(NULL, buffersize, VMM_FLAGS_ALLOCATE, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC, NULL); if (buffer == NULL) { ret.errno = ENOMEM; @@ -51,9 +58,20 @@ syscallret_t syscall_sendmsg(context_t *, int fd, msghdr_t *umsghdr, int flags) } socket_t *socket = SOCKFS_SOCKET_FROM_NODE(file->vnode); - size_t sendcount; - ret.errno = socket->ops->send(socket, msghdr.addr ? &sockaddr : NULL, buffer, buffersize, fileflagstovnodeflags(file->flags) | ((flags & MSG_NOSIGNAL) ? SOCKET_SEND_FLAGS_NOSIGNAL : 0), &sendcount); - ret.ret = ret.errno ? -1 : sendcount; + + sockdesc_t desc = { + .addr = msghdr.addr ? &sockaddr : NULL, + .buffer = buffer, + .count = buffersize, + .flags = fileflagstovnodeflags(file->flags) | ((flags & MSG_NOSIGNAL) ? SOCKET_SEND_FLAGS_NOSIGNAL : 0), + .donecount = 0, + .ctrl = msghdr.msgctrl, + .ctrllen = msghdr.ctrllen, + .ctrldone = 0 + }; + + ret.errno = socket->ops->send(socket, &desc); + ret.ret = ret.errno ? -1 : desc.donecount; cleanup: if (file)