initial recvmsg and sendmsg implementations
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20562 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -92,9 +92,11 @@ struct net_socket_module_info {
|
|||||||
ssize_t (*recv)(net_socket *socket, void *data, size_t length, int flags);
|
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,
|
ssize_t (*recvfrom)(net_socket *socket, void *data, size_t length, int flags,
|
||||||
struct sockaddr *address, socklen_t *_addressLength);
|
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 (*send)(net_socket *socket, const void *data, size_t length, int flags);
|
||||||
ssize_t (*sendto)(net_socket *socket, const void *data, size_t length,
|
ssize_t (*sendto)(net_socket *socket, const void *data, size_t length,
|
||||||
int flags, const struct sockaddr *address, socklen_t addressLength);
|
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,
|
int (*setsockopt)(net_socket *socket, int level, int option,
|
||||||
const void *optionValue, int optionLength);
|
const void *optionValue, int optionLength);
|
||||||
int (*shutdown)(net_socket *socket, int direction);
|
int (*shutdown)(net_socket *socket, int direction);
|
||||||
|
|||||||
@@ -31,8 +31,10 @@ enum {
|
|||||||
NET_STACK_BIND, // sockaddr_args *
|
NET_STACK_BIND, // sockaddr_args *
|
||||||
NET_STACK_RECVFROM, // struct msghdr *
|
NET_STACK_RECVFROM, // struct msghdr *
|
||||||
NET_STACK_RECV, // transfer_args *
|
NET_STACK_RECV, // transfer_args *
|
||||||
|
NET_STACK_RECVMSG, // msghdr_args *
|
||||||
NET_STACK_SENDTO, // struct msghdr *
|
NET_STACK_SENDTO, // struct msghdr *
|
||||||
NET_STACK_SEND, // transfer_args *
|
NET_STACK_SEND, // transfer_args *
|
||||||
|
NET_STACK_SENDMSG, // msghdr_args *
|
||||||
NET_STACK_LISTEN, // int_args * (value = backlog)
|
NET_STACK_LISTEN, // int_args * (value = backlog)
|
||||||
NET_STACK_ACCEPT, // sockaddr_args *
|
NET_STACK_ACCEPT, // sockaddr_args *
|
||||||
NET_STACK_CONNECT, // sockaddr_args *
|
NET_STACK_CONNECT, // sockaddr_args *
|
||||||
@@ -68,6 +70,11 @@ struct transfer_args { // used by NET_STACK_SEND/_RECV
|
|||||||
socklen_t address_length; // ""
|
socklen_t address_length; // ""
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct msghdr_args {
|
||||||
|
struct msghdr *header;
|
||||||
|
int flags;
|
||||||
|
};
|
||||||
|
|
||||||
struct socket_args { // used by NET_STACK_SOCKET
|
struct socket_args { // used by NET_STACK_SOCKET
|
||||||
int family;
|
int family;
|
||||||
int type;
|
int type;
|
||||||
|
|||||||
@@ -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<typename ArgType> status_t
|
template<typename ArgType> status_t
|
||||||
check_args(ArgType &args, void *data, size_t length)
|
check_args(ArgType &args, void *data, size_t length)
|
||||||
{
|
{
|
||||||
@@ -116,8 +149,10 @@ opcode_name(int op)
|
|||||||
C2N(NET_STACK_BIND),
|
C2N(NET_STACK_BIND),
|
||||||
C2N(NET_STACK_RECVFROM),
|
C2N(NET_STACK_RECVFROM),
|
||||||
C2N(NET_STACK_RECV),
|
C2N(NET_STACK_RECV),
|
||||||
|
C2N(NET_STACK_RECVMSG),
|
||||||
C2N(NET_STACK_SENDTO),
|
C2N(NET_STACK_SENDTO),
|
||||||
C2N(NET_STACK_SEND),
|
C2N(NET_STACK_SEND),
|
||||||
|
C2N(NET_STACK_SENDMSG),
|
||||||
C2N(NET_STACK_LISTEN),
|
C2N(NET_STACK_LISTEN),
|
||||||
C2N(NET_STACK_ACCEPT),
|
C2N(NET_STACK_ACCEPT),
|
||||||
C2N(NET_STACK_CONNECT),
|
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);
|
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:
|
case NET_STACK_RECV:
|
||||||
{
|
{
|
||||||
transfer_args args;
|
transfer_args args;
|
||||||
@@ -382,6 +431,32 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length)
|
|||||||
return bytesRead;
|
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:
|
case NET_STACK_GETSOCKOPT:
|
||||||
{
|
{
|
||||||
sockopt_args args;
|
sockopt_args args;
|
||||||
|
|||||||
@@ -149,8 +149,8 @@ recvfrom(int socket, void *data, size_t length, int flags,
|
|||||||
ssize_t
|
ssize_t
|
||||||
recvmsg(int socket, struct msghdr *message, int flags)
|
recvmsg(int socket, struct msghdr *message, int flags)
|
||||||
{
|
{
|
||||||
// TODO: implement me!
|
msghdr_args args = { message, flags };
|
||||||
return -1;
|
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
|
ssize_t
|
||||||
sendmsg(int socket, const struct msghdr *message, int flags)
|
sendmsg(int socket, const struct msghdr *message, int flags)
|
||||||
{
|
{
|
||||||
// TODO: implement me!
|
msghdr_args args = { (msghdr *)message, flags };
|
||||||
return -1;
|
return ioctl(socket, NET_STACK_SENDMSG, &args, sizeof(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
ssize_t
|
||||||
socket_send(net_socket *socket, const void *data, size_t length, int flags)
|
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
|
int
|
||||||
socket_setsockopt(net_socket *socket, int level, int option, const void *value,
|
socket_setsockopt(net_socket *socket, int level, int option, const void *value,
|
||||||
int length)
|
int length)
|
||||||
@@ -1080,8 +1210,10 @@ net_socket_module_info gNetSocketModule = {
|
|||||||
socket_listen,
|
socket_listen,
|
||||||
socket_recv,
|
socket_recv,
|
||||||
socket_recvfrom,
|
socket_recvfrom,
|
||||||
|
socket_recvmsg,
|
||||||
socket_send,
|
socket_send,
|
||||||
socket_sendto,
|
socket_sendto,
|
||||||
|
socket_sendmsg,
|
||||||
socket_setsockopt,
|
socket_setsockopt,
|
||||||
socket_shutdown,
|
socket_shutdown,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -341,8 +341,8 @@ recvfrom(int socket, void *data, size_t length, int flags,
|
|||||||
extern "C" ssize_t
|
extern "C" ssize_t
|
||||||
recvmsg(int socket, struct msghdr *message, int flags)
|
recvmsg(int socket, struct msghdr *message, int flags)
|
||||||
{
|
{
|
||||||
// TODO: implement me!
|
msghdr_args args = { message, flags };
|
||||||
return -1;
|
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
|
extern "C" ssize_t
|
||||||
sendmsg(int socket, const struct msghdr *message, int flags)
|
sendmsg(int socket, const struct msghdr *message, int flags)
|
||||||
{
|
{
|
||||||
// TODO: implement me!
|
msghdr_args args = { (msghdr *)message, flags };
|
||||||
return -1;
|
return ioctl(socket, NET_STACK_SENDMSG, &args, sizeof(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user