From dae7f8ea467af95879f02fbc4d7dc4d804fea799 Mon Sep 17 00:00:00 2001 From: Hugo Santos Date: Wed, 4 Apr 2007 17:39:43 +0000 Subject: [PATCH] initial recvmsg and sendmsg implementations git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20562 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/net/net_socket.h | 2 + headers/private/net/net_stack_driver.h | 7 + .../drivers/network/stack/kernel_stack.cpp | 75 ++++++++++ src/add-ons/kernel/network/socket/socket.cpp | 8 +- .../kernel/network/stack/net_socket.cpp | 132 ++++++++++++++++++ src/kits/network/socket.cpp | 8 +- 6 files changed, 224 insertions(+), 8 deletions(-) diff --git a/headers/private/net/net_socket.h b/headers/private/net/net_socket.h index 0eca484cd1..e9cf4079d4 100644 --- a/headers/private/net/net_socket.h +++ b/headers/private/net/net_socket.h @@ -92,9 +92,11 @@ struct net_socket_module_info { ssize_t (*recv)(net_socket *socket, void *data, size_t length, int flags); ssize_t (*recvfrom)(net_socket *socket, void *data, size_t length, int flags, struct sockaddr *address, socklen_t *_addressLength); + ssize_t (*recvmsg)(net_socket *socket, msghdr *header, int flags); ssize_t (*send)(net_socket *socket, const void *data, size_t length, int flags); ssize_t (*sendto)(net_socket *socket, const void *data, size_t length, int flags, const struct sockaddr *address, socklen_t addressLength); + ssize_t (*sendmsg)(net_socket *socket, msghdr *header, int flags); int (*setsockopt)(net_socket *socket, int level, int option, const void *optionValue, int optionLength); int (*shutdown)(net_socket *socket, int direction); diff --git a/headers/private/net/net_stack_driver.h b/headers/private/net/net_stack_driver.h index f778be1fcb..df887010c3 100644 --- a/headers/private/net/net_stack_driver.h +++ b/headers/private/net/net_stack_driver.h @@ -31,8 +31,10 @@ enum { NET_STACK_BIND, // sockaddr_args * NET_STACK_RECVFROM, // struct msghdr * NET_STACK_RECV, // transfer_args * + NET_STACK_RECVMSG, // msghdr_args * NET_STACK_SENDTO, // struct msghdr * NET_STACK_SEND, // transfer_args * + NET_STACK_SENDMSG, // msghdr_args * NET_STACK_LISTEN, // int_args * (value = backlog) NET_STACK_ACCEPT, // sockaddr_args * NET_STACK_CONNECT, // sockaddr_args * @@ -68,6 +70,11 @@ struct transfer_args { // used by NET_STACK_SEND/_RECV socklen_t address_length; // "" }; +struct msghdr_args { + struct msghdr *header; + int flags; +}; + struct socket_args { // used by NET_STACK_SOCKET int family; int type; diff --git a/src/add-ons/kernel/drivers/network/stack/kernel_stack.cpp b/src/add-ons/kernel/drivers/network/stack/kernel_stack.cpp index 7662bf399c..e2e24d256b 100644 --- a/src/add-ons/kernel/drivers/network/stack/kernel_stack.cpp +++ b/src/add-ons/kernel/drivers/network/stack/kernel_stack.cpp @@ -89,6 +89,39 @@ check_args_and_address(ArgType &args, sockaddr_storage &address, void *data, siz } +static status_t +check_msghdr_args(msghdr_args &args, msghdr &header, sockaddr_storage &address, + void *data, size_t length, sockaddr **originalAddress) +{ + if (length < sizeof(msghdr_args)) + return B_BAD_VALUE; + + status_t status = user_memcpy(&args, data, sizeof(msghdr_args)); + if (status < B_OK) + return status; + + status = user_memcpy(&header, args.header, sizeof(msghdr)); + if (status < B_OK) + return status; + + if (originalAddress == NULL) { + if (header.msg_namelen > sizeof(address)) + return B_BAD_VALUE; + + if (header.msg_name != NULL) { + status = user_memcpy(&address, header.msg_name, header.msg_namelen); + if (status < B_OK) + return B_BAD_ADDRESS; + } + } else { + originalAddress = (sockaddr **)&header.msg_name; + } + + if (header.msg_name != NULL) + header.msg_name = (char *)&address; + return B_OK; +} + template status_t check_args(ArgType &args, void *data, size_t length) { @@ -116,8 +149,10 @@ opcode_name(int op) C2N(NET_STACK_BIND), C2N(NET_STACK_RECVFROM), C2N(NET_STACK_RECV), + C2N(NET_STACK_RECVMSG), C2N(NET_STACK_SENDTO), C2N(NET_STACK_SEND), + C2N(NET_STACK_SENDMSG), C2N(NET_STACK_LISTEN), C2N(NET_STACK_ACCEPT), C2N(NET_STACK_CONNECT), @@ -353,6 +388,20 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length) args.flags, args.address, args.address_length); } + case NET_STACK_SENDMSG: + { + sockaddr_storage address; + msghdr_args args; + msghdr header; + + status = check_msghdr_args(args, header, address, data, + length, NULL); + if (status < B_OK) + return status; + + return sSocket->sendmsg(cookie->socket, &header, args.flags); + } + case NET_STACK_RECV: { transfer_args args; @@ -382,6 +431,32 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length) return bytesRead; } + case NET_STACK_RECVMSG: + { + sockaddr *originalAddress; + sockaddr_storage address; + msghdr_args args; + msghdr header; + + status = check_msghdr_args(args, header, address, data, + length, &originalAddress); + if (status < B_OK) + return status; + + ssize_t bytesRead = sSocket->recvmsg(cookie->socket, &header, + args.flags); + if (bytesRead < B_OK) + return bytesRead; + + if (header.msg_name != NULL) { + if (user_memcpy(originalAddress, header.msg_name, + header.msg_namelen) < B_OK) + return B_BAD_ADDRESS; + } + + return bytesRead; + } + case NET_STACK_GETSOCKOPT: { sockopt_args args; diff --git a/src/add-ons/kernel/network/socket/socket.cpp b/src/add-ons/kernel/network/socket/socket.cpp index bc68b5370e..3dec39747b 100644 --- a/src/add-ons/kernel/network/socket/socket.cpp +++ b/src/add-ons/kernel/network/socket/socket.cpp @@ -149,8 +149,8 @@ recvfrom(int socket, void *data, size_t length, int flags, ssize_t recvmsg(int socket, struct msghdr *message, int flags) { - // TODO: implement me! - return -1; + msghdr_args args = { message, flags }; + return ioctl(socket, NET_STACK_RECVMSG, &args, sizeof(args)); } @@ -186,8 +186,8 @@ sendto(int socket, const void *data, size_t length, int flags, ssize_t sendmsg(int socket, const struct msghdr *message, int flags) { - // TODO: implement me! - return -1; + msghdr_args args = { (msghdr *)message, flags }; + return ioctl(socket, NET_STACK_SENDMSG, &args, sizeof(args)); } diff --git a/src/add-ons/kernel/network/stack/net_socket.cpp b/src/add-ons/kernel/network/stack/net_socket.cpp index a3013c7663..2f4f490c70 100644 --- a/src/add-ons/kernel/network/stack/net_socket.cpp +++ b/src/add-ons/kernel/network/stack/net_socket.cpp @@ -782,6 +782,70 @@ socket_recvfrom(net_socket *socket, void *data, size_t length, int flags, } +ssize_t +socket_recvmsg(net_socket *socket, msghdr *header, int flags) +{ + net_buffer *buffer; + iovec tmp; + int i; + + size_t length = 0; + for (i = 0; i < header->msg_iovlen; i++) { + if (user_memcpy(&tmp, header->msg_iov + i, sizeof(iovec)) < B_OK) + return B_BAD_ADDRESS; + if (tmp.iov_len > 0 && tmp.iov_base == NULL) + return B_BAD_ADDRESS; + length += tmp.iov_len; + } + + status_t status = socket->first_info->read_data( + socket->first_protocol, length, flags, &buffer); + if (status < B_OK) + return status; + + // TODO: - consider the control buffer options + // - datagram based protocols should return the + // full datagram so we can cut it here with MSG_TRUNC + // - returning a NULL buffer when received 0 bytes + // may not make much sense as we still need the address + + header->msg_namelen = 0; + header->msg_flags = 0; + + if (buffer == NULL) + return 0; + + size_t bytesReceived = 0; + for (i = 0; i < header->msg_iovlen && bytesReceived < buffer->size; i++) { + if (user_memcpy(&tmp, header->msg_iov + i, sizeof(iovec)) != B_OK) + break; + + size_t toRead = min_c(buffer->size - bytesReceived, tmp.iov_len); + + if (gNetBufferModule.read(buffer, bytesReceived, tmp.iov_base, + toRead) < B_OK) + break; + + bytesReceived += toRead; + } + + if (bytesReceived == buffer->size) { + if (header->msg_namelen >= buffer->source.ss_len) { + memcpy(header->msg_name, &buffer->source, buffer->source.ss_len); + header->msg_namelen = buffer->source.ss_len; + } + } + + size_t bufferSize = buffer->size; + gNetBufferModule.free(buffer); + + if (bytesReceived < bufferSize) + return ENOBUFS; + + return bytesReceived; +} + + ssize_t socket_send(net_socket *socket, const void *data, size_t length, int flags) { @@ -885,6 +949,72 @@ socket_sendto(net_socket *socket, const void *data, size_t length, int flags, } +status_t +socket_sendmsg(net_socket *socket, msghdr *header, int flags) +{ + const sockaddr *address = (const sockaddr *)header->msg_name; + socklen_t addressLength = header->msg_namelen; + + if ((address == NULL || addressLength == 0) && socket->peer.ss_len != 0) { + // socket is connected, we use that address: + address = (struct sockaddr *)&socket->peer; + addressLength = socket->peer.ss_len; + } + if (address == NULL || addressLength == 0) { + // don't know where to send to: + return EDESTADDRREQ; + } + if (socket->peer.ss_len != 0) { + // an address has been given but socket is connected already: + return EISCONN; + } + + if (socket->address.ss_len == 0) { + // try to bind first + status_t status = socket_bind(socket, NULL, 0); + if (status < B_OK) + return status; + } + + // TODO: useful, maybe even computed header space! + net_buffer *buffer = gNetBufferModule.create(256); + if (buffer == NULL) + return ENOBUFS; + + size_t length = 0; + + // copy data into buffer + for (int i = 0; i < header->msg_iovlen; i++) { + iovec tmp; + if (user_memcpy(&tmp, header->msg_iov + i, sizeof(iovec)) < B_OK || + gNetBufferModule.append(buffer, tmp.iov_base, tmp.iov_len) < B_OK) { + gNetBufferModule.free(buffer); + return ENOBUFS; + } + + length += tmp.iov_len; + } + + memcpy(&buffer->source, &socket->address, socket->address.ss_len); + memcpy(&buffer->destination, &socket->peer, socket->peer.ss_len); + + status_t status = socket->first_info->send_data(socket->first_protocol, + buffer); + if (status < B_OK) { + size_t size = buffer->size; + gNetBufferModule.free(buffer); + + if (size != length && (status == B_INTERRUPTED || status == B_WOULD_BLOCK)) { + // this appears to be a partial write + return length - size; + } + return status; + } + + return length; +} + + int socket_setsockopt(net_socket *socket, int level, int option, const void *value, int length) @@ -1080,8 +1210,10 @@ net_socket_module_info gNetSocketModule = { socket_listen, socket_recv, socket_recvfrom, + socket_recvmsg, socket_send, socket_sendto, + socket_sendmsg, socket_setsockopt, socket_shutdown, }; diff --git a/src/kits/network/socket.cpp b/src/kits/network/socket.cpp index 8ca7fcef80..ac779835d7 100644 --- a/src/kits/network/socket.cpp +++ b/src/kits/network/socket.cpp @@ -341,8 +341,8 @@ recvfrom(int socket, void *data, size_t length, int flags, extern "C" ssize_t recvmsg(int socket, struct msghdr *message, int flags) { - // TODO: implement me! - return -1; + msghdr_args args = { message, flags }; + return ioctl(socket, NET_STACK_RECVMSG, &args, sizeof(args)); } @@ -386,8 +386,8 @@ sendto(int socket, const void *data, size_t length, int flags, extern "C" ssize_t sendmsg(int socket, const struct msghdr *message, int flags) { - // TODO: implement me! - return -1; + msghdr_args args = { (msghdr *)message, flags }; + return ioctl(socket, NET_STACK_SENDMSG, &args, sizeof(args)); }