Skip to content

Commit

Permalink
Initial recvmsg(3) implementation (fixes traceroute)
Browse files Browse the repository at this point in the history
  • Loading branch information
Philipp Wallisch committed Dec 20, 2018
1 parent 0a51d8f commit 5a4af30
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 2 deletions.
124 changes: 123 additions & 1 deletion fs/sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,128 @@ dword_t sys_getsockopt(fd_t sock_fd, dword_t level, dword_t option, addr_t value
return 0;
}

dword_t sys_recvmsg(fd_t sock_fd, addr_t msghdr_addr, dword_t flags) {
STRACE("recvmsg(%d, 0x%x, %d)", sock_fd, msghdr_addr, flags);
struct fd *sock = sock_getfd(sock_fd);
if (sock == NULL)
return _EBADF;

struct msghdr msg;
memset(&msg, 0, sizeof(msg));

struct msghdr_ msg_fake;
if (user_get(msghdr_addr, msg_fake)) {
return _EFAULT;
}

// msg_name
struct sockaddr msg_name;
memset(&msg_name, 0, sizeof(msg_name));

struct sockaddr_ msg_name_fake;
if (msg_fake.msg_name != 0 && msg_fake.msg_namelen != 0) {
if (user_get(msg_fake.msg_name, msg_name_fake))
return _EFAULT;
#ifdef __APPLE__
msg_name.sa_len = sizeof(msg_name);
#endif
msg_name.sa_family = msg_name_fake.family;
memcpy(&msg_name.sa_data, msg_name_fake.data, sizeof(msg_name_fake.data));

msg.msg_name = (void *)&msg_name;
msg.msg_namelen = sizeof(msg_name);
}

// msg_control (no initial content)
char msg_control[msg_fake.msg_controllen];
memset(&msg_control, 0, sizeof(msg_control));

msg.msg_control = (void *)&msg_control;
msg.msg_controllen = sizeof(msg_control);

// msg_iovec (no initial content)
struct iovec msg_iov[msg_fake.msg_iovlen];
memset(&msg_iov, 0, sizeof(msg_iov));

struct iovec_ msg_iov_fake[msg_fake.msg_iovlen];
if (user_get(msg_fake.msg_iov, msg_iov_fake))
return _EFAULT;

for (int i = 0; i < msg_fake.msg_iovlen; i++) {
char iov_base[msg_iov_fake[i].iov_len];
memset(&iov_base, 0, sizeof(iov_base));

msg_iov[i].iov_base = (void *)&iov_base;
msg_iov[i].iov_len = sizeof(iov_base);
}

msg.msg_iov = (void *)&msg_iov;
msg.msg_iovlen = msg_fake.msg_iovlen;

// msg_flags (no initial content)
msg.msg_flags = sock_flags_to_real(msg_fake.msg_flags);

int real_flags = sock_flags_to_real(flags);
if (real_flags < 0)
return _EINVAL;

ssize_t res = recvmsg(sock->real_fd, &msg, real_flags);

// msg_name (changed)
memset(&msg_name_fake, 0, sizeof(msg_name_fake));

if (msg.msg_name != 0 && msg.msg_namelen != 0) {
struct sockaddr *msg_name = msg.msg_name;

msg_name_fake.family = msg_name->sa_family;
memcpy(&msg_name_fake.data, msg_name->sa_data, sizeof(msg_name->sa_data));

if (user_put(msg_fake.msg_name, msg_name_fake)) {
return _EFAULT;
}
msg_fake.msg_namelen = sizeof(msg_name_fake);
}

// msg_control (changed)
uint_t message_offset = 0;
for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
void *cmsg_data = CMSG_DATA(cmsg);
uint_t cmsg_data_size = cmsg->cmsg_len - sizeof(cmsg);

if (user_write(msg_fake.msg_control + message_offset, cmsg, sizeof(cmsg)))
return _EFAULT;

if (user_write(msg_fake.msg_control + message_offset + sizeof(cmsg), cmsg_data, cmsg_data_size))
return _EFAULT;

message_offset += cmsg->cmsg_len;
}

msg_fake.msg_controllen = message_offset;

// msg_iovec (changed)
for (int i = 0; i < msg.msg_iovlen; i++) {
if (user_write(msg_iov_fake[i].iov_base, msg_iov[i].iov_base, msg_iov[i].iov_len))
return _EFAULT;

msg_iov_fake[i].iov_len = msg_iov[i].iov_len;
}

if (user_put(msg_fake.msg_iov, msg_iov_fake))
return _EFAULT;

// msg_flags (changed)
msg_fake.msg_flags = sock_flags_from_real(msg.msg_flags);

if (user_put(msghdr_addr, msg_fake)) {
return _EFAULT;
}

if (res < 0)
return errno_map();
return res;
}

static struct fd_ops socket_fdops = {
.read = realfs_read,
.write = realfs_write,
Expand Down Expand Up @@ -398,7 +520,7 @@ static struct socket_call {
{(syscall_t) sys_setsockopt, 5},
{(syscall_t) sys_getsockopt, 5},
{NULL}, // sendmsg
{NULL}, // recvmsg
{(syscall_t) sys_recvmsg, 3},
{NULL}, // accept4
{NULL}, // recvmmsg
{NULL}, // sendmmsg
Expand Down
42 changes: 41 additions & 1 deletion fs/sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ size_t sockaddr_size(void *p);
// result comes from malloc
struct sockaddr *sockaddr_to_real(void *p);

struct msghdr_ {
addr_t msg_name;
int_t msg_namelen;
addr_t msg_iov;
uint_t msg_iovlen;
addr_t msg_control;
uint_t msg_controllen;
int_t msg_flags;
};

struct iovec_ {
addr_t iov_base;
uint_t iov_len;
};

#define PF_LOCAL_ 1
#define PF_INET_ 2
#define PF_INET6_ 10
Expand Down Expand Up @@ -69,17 +84,38 @@ static inline int sock_type_to_real(int type, int protocol) {

#define MSG_OOB_ 0x1
#define MSG_PEEK_ 0x2
#define MSG_CTRUNC_ 0x8
#define MSG_TRUNC_ 0x20
#define MSG_DONTWAIT_ 0x40
#define MSG_EOR_ 0x80
#define MSG_WAITALL_ 0x100

static inline int sock_flags_to_real(int fake) {
int real = 0;
if (fake & MSG_OOB_) real |= MSG_OOB;
if (fake & MSG_PEEK_) real |= MSG_PEEK;
if (fake & MSG_CTRUNC_) real |= MSG_CTRUNC;
if (fake & MSG_TRUNC_) real |= MSG_TRUNC;
if (fake & MSG_DONTWAIT_) real |= MSG_DONTWAIT;
if (fake & MSG_EOR_) real |= MSG_EOR;
if (fake & MSG_WAITALL_) real |= MSG_WAITALL;
if (fake & ~(MSG_OOB_|MSG_PEEK_|MSG_WAITALL_))
if (fake & ~(MSG_OOB_|MSG_PEEK_|MSG_CTRUNC_|MSG_TRUNC_|MSG_DONTWAIT_|MSG_EOR_|MSG_WAITALL_))
TRACE("unimplemented socket flags %d\n", fake);
return real;
}
static inline int sock_flags_from_real(int real) {
int fake = 0;
if (real & MSG_OOB) fake |= MSG_OOB_;
if (real & MSG_PEEK) fake |= MSG_PEEK_;
if (real & MSG_CTRUNC) fake |= MSG_CTRUNC_;
if (real & MSG_TRUNC) fake |= MSG_TRUNC_;
if (real & MSG_DONTWAIT) fake |= MSG_DONTWAIT_;
if (real & MSG_EOR) fake |= MSG_EOR_;
if (real & MSG_WAITALL) fake |= MSG_WAITALL_;
if (real & ~(MSG_OOB|MSG_PEEK|MSG_CTRUNC|MSG_TRUNC|MSG_DONTWAIT|MSG_EOR|MSG_WAITALL))
TRACE("unimplemented socket flags %d\n", real);
return fake;
}

#define SOL_SOCKET_ 1
#define IPPROTO_TCP_ 6
Expand All @@ -93,7 +129,9 @@ static inline int sock_flags_to_real(int fake) {
#define SO_ERROR_ 4
#define SO_BROADCAST_ 6
#define SO_KEEPALIVE_ 9
#define SO_SNDBUF_ 7
#define IP_TOS_ 1
#define IP_TTL_ 2
#define TCP_NODELAY_ 1
#define IPV6_TCLASS_ 67
#define ICMP6_FILTER_ 1
Expand All @@ -106,12 +144,14 @@ static inline int sock_opt_to_real(int fake, int level) {
case SO_ERROR_: return SO_ERROR;
case SO_BROADCAST_: return SO_BROADCAST;
case SO_KEEPALIVE_: return SO_KEEPALIVE;
case SO_SNDBUF_: return SO_SNDBUF;
} break;
case IPPROTO_TCP_: switch (fake) {
case TCP_NODELAY_: return TCP_NODELAY;
} break;
case IPPROTO_IP_: switch (fake) {
case IP_TOS_: return IP_TOS;
case IP_TTL_: return IP_TTL;
} break;
case IPPROTO_IPV6_: switch (fake) {
case IPV6_TCLASS_: return IPV6_TCLASS;
Expand Down

0 comments on commit 5a4af30

Please sign in to comment.