Skip to content

Support connect() for tcp wrapper#2764

Merged
stevenengler merged 2 commits intoshadow:mainfrom
stevenengler:wrap-tcp-rust
Feb 25, 2023
Merged

Support connect() for tcp wrapper#2764
stevenengler merged 2 commits intoshadow:mainfrom
stevenengler:wrap-tcp-rust

Conversation

@stevenengler
Copy link
Copy Markdown
Contributor

The logic generally follows the logic from src/main/host/syscall/socket.c.

SysCallReturn syscallhandler_connect(SysCallHandler* sys,
const SysCallArgs* args) {
int sockfd = args->args[0].as_i64;
PluginPtr addrPtr = args->args[1].as_ptr; // const struct sockaddr*
socklen_t addrlen = args->args[2].as_u64;
trace("trying to connect on socket %i", sockfd);
/* Get and validate the socket. */
LegacySocket* socket_desc = NULL;
int errcode =
_syscallhandler_validateSocketHelper(sys, sockfd, &socket_desc);
if (errcode < 0) {
return syscallreturn_makeDoneErrno(-errcode);
}
utility_debugAssert(socket_desc);
/* TODO: we assume AF_INET here, change this when we support AF_UNIX */
// size_t unix_len = sizeof(struct sockaddr_un); // if sa_family==AF_UNIX
size_t inet_len = sizeof(struct sockaddr_in);
if (addrlen < inet_len) {
return syscallreturn_makeDoneErrno(EINVAL);
}
const struct sockaddr* addr =
process_getReadablePtr(_syscallhandler_getProcess(sys), addrPtr, addrlen);
if (!addr) {
debug("Couldn't read addr %p", (void*)addrPtr.val);
return syscallreturn_makeDoneErrno(EFAULT);
}
/* TODO: we assume AF_INET here, change this when we support AF_UNIX */
if (addr->sa_family != AF_INET && addr->sa_family != AF_UNSPEC) {
warning("connecting to address family %i, but we only support AF_INET",
(int)addr->sa_family);
return syscallreturn_makeDoneErrno(EAFNOSUPPORT);
} else if (!legacysocket_isFamilySupported(socket_desc, addr->sa_family)) {
return syscallreturn_makeDoneErrno(EAFNOSUPPORT);
}
/* TODO: update for AF_UNIX */
struct sockaddr_in* inet_addr = (struct sockaddr_in*)addr;
sa_family_t family = inet_addr->sin_family;
in_addr_t peerAddr = inet_addr->sin_addr.s_addr;
in_port_t peerPort = inet_addr->sin_port;
in_addr_t loopbackAddr = htonl(INADDR_LOOPBACK);
if (peerAddr == htonl(INADDR_ANY)) {
peerAddr = loopbackAddr;
}
/* make sure we will be able to route this later */
if (peerAddr != loopbackAddr) {
const Address* myAddress = host_getDefaultAddress(_syscallhandler_getHost(sys));
const Address* peerAddress = worker_resolveIPToAddress(peerAddr);
in_addr_t myAddr = htonl(address_toHostIP(myAddress));
if (!peerAddress || !worker_isRoutable(myAddr, peerAddr)) {
/* can't route it - there is no node with this address */
gchar* peerAddressString = address_ipToNewString(peerAddr);
warning("attempting to connect to address '%s:%u' for which no "
"host exists",
peerAddressString, ntohs(peerPort));
g_free(peerAddressString);
return syscallreturn_makeDoneErrno(ECONNREFUSED);
}
}
if (!legacysocket_isBound(socket_desc)) {
/* do an implicit bind to a random ephemeral port.
* use default interface unless the remote peer is on loopback */
in_addr_t bindAddr = (loopbackAddr == peerAddr)
? loopbackAddr
: host_getDefaultIP(_syscallhandler_getHost(sys));
errcode = _syscallhandler_bindHelper(
sys, socket_desc, bindAddr, 0, peerAddr, peerPort);
if (errcode < 0) {
return syscallreturn_makeDoneErrno(-errcode);
}
} else {
legacysocket_setPeerName(socket_desc, peerAddr, peerPort);
}
/* Now we are ready to connect. */
errcode = legacysocket_connectToPeer(
socket_desc, _syscallhandler_getHost(sys), peerAddr, peerPort, family);
LegacyFile* desc = (LegacyFile*)socket_desc;
if (legacyfile_getType(desc) == DT_TCPSOCKET && !(legacyfile_getFlags(desc) & O_NONBLOCK)) {
/* This is a blocking connect call. */
if (errcode == -EINPROGRESS) {
/* This is the first time we ever called connect, and so we
* need to wait for the 3-way handshake to complete.
* We will wait indefinitely for a success or failure. */
Trigger trigger = (Trigger){.type = TRIGGER_DESCRIPTOR,
.object = desc,
.status = STATUS_FILE_ACTIVE | STATUS_FILE_WRITABLE};
return syscallreturn_makeBlocked(
syscallcondition_new(trigger), legacyfile_supportsSaRestart(desc));
} else if (_syscallhandler_wasBlocked(sys) && errcode == -EISCONN) {
/* It was EINPROGRESS, but is now a successful blocking connect. */
errcode = 0;
}
}
/* Make sure we return valid error codes for connect. */
if (errcode == -ECONNRESET || errcode == -ENOTCONN) {
errcode = -EISCONN;
}
/* -EALREADY is well defined in man page, but Linux returns -EINPROGRESS. */
else if (errcode == -EALREADY) {
errcode = -EINPROGRESS;
}
/* Return 0, -EINPROGRESS, etc. now. */
return syscallreturn_makeDoneI64(errcode);
}

@stevenengler stevenengler self-assigned this Feb 24, 2023
@github-actions github-actions bot added the Component: Main Composing the core Shadow executable label Feb 24, 2023
@stevenengler stevenengler merged commit 1c2d436 into shadow:main Feb 25, 2023
@stevenengler stevenengler deleted the wrap-tcp-rust branch February 25, 2023 00:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Component: Main Composing the core Shadow executable

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants