gint host_epollControl(Host* host, gint epollDescriptor, gint operation, gint fileDescriptor, struct epoll_event* event) { MAGIC_ASSERT(host); /* EBADF epfd is not a valid file descriptor. */ Descriptor* descriptor = host_lookupDescriptor(host, epollDescriptor); if(descriptor == NULL) { return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", epollDescriptor); return EBADF; } /* EINVAL epfd is not an epoll file descriptor */ if(descriptor_getType(descriptor) != DT_EPOLL) { return EINVAL; } /* now we know its an epoll */ Epoll* epoll = (Epoll*) descriptor; /* if this is for a system file, forward to system call */ if(!host_isShadowDescriptor(host, fileDescriptor)) { gint osfd = host_getOSHandle(host, fileDescriptor); osfd = osfd >= 0 ? osfd : fileDescriptor; gint oldEventFD = event->data.fd; event->data.fd = osfd; gint result = epoll_controlOS(epoll, operation, osfd, event); event->data.fd = oldEventFD; return result; } /* EBADF fd is not a valid shadow file descriptor. */ descriptor = host_lookupDescriptor(host, fileDescriptor); if(descriptor == NULL) { return EBADF; } status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", fileDescriptor); return EBADF; } return epoll_control(epoll, operation, descriptor, event); }
gint host_epollGetEvents(Host* host, gint handle, struct epoll_event* eventArray, gint eventArrayLength, gint* nEvents) { MAGIC_ASSERT(host); /* EBADF epfd is not a valid file descriptor. */ Descriptor* descriptor = host_lookupDescriptor(host, handle); if(descriptor == NULL) { return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", handle); return EBADF; } /* EINVAL epfd is not an epoll file descriptor */ if(descriptor_getType(descriptor) != DT_EPOLL) { return EINVAL; } Epoll* epoll = (Epoll*) descriptor; gint ret = epoll_getEvents(epoll, eventArray, eventArrayLength, nEvents); for(gint i = 0; i < *nEvents; i++) { if(!host_isShadowDescriptor(host, eventArray[i].data.fd)) { /* the fd is a file that the OS handled for us, translate to shadow fd */ eventArray[i].data.fd = host_getShadowHandle(host, eventArray[i].data.fd); utility_assert(eventArray[i].data.fd >= 0); } } return ret; }
static gint _host_monitorDescriptor(Host* host, Descriptor* descriptor) { MAGIC_ASSERT(host); /* make sure there are no collisions before inserting */ gint* handle = descriptor_getHandleReference(descriptor); utility_assert(handle && !host_lookupDescriptor(host, *handle)); g_hash_table_replace(host->descriptors, handle, descriptor); return *handle; }
void notifyplugin_run(NotifyPluginEvent* event, Host* node) { MAGIC_ASSERT(event); debug("event started"); /* check in with epoll to make sure we should carry out the notification */ Epoll* epoll = (Epoll*) host_lookupDescriptor(node, event->epollHandle); epoll_tryNotify(epoll); debug("event finished"); }
static void _host_unmonitorDescriptor(Host* host, gint handle) { MAGIC_ASSERT(host); Descriptor* descriptor = host_lookupDescriptor(host, handle); if(descriptor) { if(descriptor->type == DT_TCPSOCKET || descriptor->type == DT_UDPSOCKET) { Socket* socket = (Socket*) descriptor; _host_disassociateInterface(host, socket); } g_hash_table_remove(host->descriptors, (gconstpointer) &handle); } }
gint system_socketPair(gint domain, gint type, gint protocol, gint fds[2]) { /* create a pair of connected sockets, i.e. a bi-directional pipe */ if(domain != AF_UNIX) { errno = EAFNOSUPPORT; return -1; } /* only support non-blocking sockets */ gboolean isBlocking = FALSE; /* clear non-blocking flags if set to get true type */ gint realType = type; if(realType & SOCK_NONBLOCK) { realType = realType & ~SOCK_NONBLOCK; isBlocking = FALSE; } if(realType & SOCK_CLOEXEC) { realType = realType & ~SOCK_CLOEXEC; isBlocking = FALSE; } if(realType != SOCK_STREAM) { errno = EPROTONOSUPPORT; return -1; } gint result = 0; Host* node = _system_switchInShadowContext(); if(isBlocking) { warning("we only support non-blocking sockets: please bitwise OR 'SOCK_NONBLOCK' with type flags"); errno = EPROTONOSUPPORT; result = -1; } if(result == 0) { gint handle = host_createDescriptor(node, DT_SOCKETPAIR); Channel* channel = (Channel*) host_lookupDescriptor(node, handle); gint linkedHandle = channel_getLinkedHandle(channel); fds[0] = handle; fds[1] = linkedHandle; } _system_switchOutShadowContext(node); return result; }
gint system_pipe2(gint pipefds[2], gint flags) { /* we only support non-blocking sockets, and require * SOCK_NONBLOCK to be set immediately */ gboolean isBlocking = TRUE; /* clear non-blocking flags if set to get true type */ if(flags & O_NONBLOCK) { flags = flags & ~O_NONBLOCK; isBlocking = FALSE; } if(flags & O_CLOEXEC) { flags = flags & ~O_CLOEXEC; isBlocking = FALSE; } Host* node = _system_switchInShadowContext(); gint result = 0; /* check inputs for what we support */ if(isBlocking) { warning("we only support non-blocking pipes: please bitwise OR 'O_NONBLOCK' with flags"); result = EINVAL; } else { gint handle = host_createDescriptor(node, DT_PIPE); Channel* channel = (Channel*) host_lookupDescriptor(node, handle); gint linkedHandle = channel_getLinkedHandle(channel); pipefds[0] = handle; /* reader */ pipefds[1] = linkedHandle; /* writer */ } _system_switchOutShadowContext(node); if(result != 0) { errno = result; return -1; } return 0; }
gint system_ioctl(int fd, unsigned long int request, va_list farg) { /* check if this is a socket */ if(fd < MIN_DESCRIPTOR){ errno = EBADF; return -1; } gint result = 0; /* normally, the type of farg depends on the request */ Host* node = _system_switchInShadowContext(); Descriptor* descriptor = host_lookupDescriptor(node, fd); if(descriptor) { DescriptorType t = descriptor_getType(descriptor); if(t == DT_TCPSOCKET || t == DT_UDPSOCKET) { Socket* socket = (Socket*) descriptor; if(request == SIOCINQ || request == FIONREAD) { gsize bufferLength = socket_getInputBufferLength(socket); gint* lengthOut = va_arg(farg, int*); *lengthOut = (gint)bufferLength; } else if (request == SIOCOUTQ || request == TIOCOUTQ) {
gint host_acceptNewPeer(Host* host, gint handle, in_addr_t* ip, in_port_t* port, gint* acceptedHandle) { MAGIC_ASSERT(host); Descriptor* descriptor = host_lookupDescriptor(host, handle); if(descriptor == NULL) { warning("descriptor handle '%i' not found", handle); return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", handle); return EBADF; } DescriptorType type = descriptor_getType(descriptor); if(type != DT_TCPSOCKET) { return EOPNOTSUPP; } return tcp_acceptServerPeer((TCP*)descriptor, ip, port, acceptedHandle); }
gint host_listenForPeer(Host* host, gint handle, gint backlog) { MAGIC_ASSERT(host); Descriptor* descriptor = host_lookupDescriptor(host, handle); if(descriptor == NULL) { warning("descriptor handle '%i' not found", handle); return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", handle); return EBADF; } DescriptorType type = descriptor_getType(descriptor); if(type != DT_TCPSOCKET) { warning("wrong type for descriptor handle '%i'", handle); return EOPNOTSUPP; } Socket* socket = (Socket*) descriptor; TCP* tcp = (TCP*) descriptor; if(!socket_isBound(socket)) { /* implicit bind */ in_addr_t bindAddress = htonl(INADDR_ANY); in_port_t bindPort = _host_getRandomFreePort(host, bindAddress, type); if(!bindPort) { return EADDRNOTAVAIL; } _host_associateInterface(host, socket, bindAddress, bindPort); } tcp_enterServerMode(tcp, backlog); return 0; }
gint host_getPeerName(Host* host, gint handle, const struct sockaddr* address, socklen_t* len) { MAGIC_ASSERT(host); Descriptor* descriptor = host_lookupDescriptor(host, handle); if(descriptor == NULL) { warning("descriptor handle '%i' not found", handle); return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", handle); return EBADF; } DescriptorType type = descriptor_getType(descriptor); if(type != DT_TCPSOCKET) { return ENOTCONN; } Socket* sock = (Socket*)descriptor; in_addr_t ip = 0; in_port_t port = 0; gboolean hasPeer = socket_getPeerName(sock, &ip, &port); if(hasPeer) { if(socket_isUnix(sock)) { struct sockaddr_un* saddr = (struct sockaddr_un*) address; saddr->sun_family = AF_UNIX; gchar* unixPath = socket_getUnixPath(sock); if(unixPath) { g_snprintf(saddr->sun_path, 107, "%s\\0", unixPath); *len = offsetof(struct sockaddr_un, sun_path) + strlen(saddr->sun_path) + 1; } else { *len = sizeof(sa_family_t); } } else {
gint system_setSockOpt(gint fd, gint level, gint optname, const gpointer optval, socklen_t optlen) { if(!optval) { errno = EFAULT; return -1; } Host* node = _system_switchInShadowContext(); Descriptor* descriptor = host_lookupDescriptor(node, fd); gint result = 0; /* TODO: implement socket options */ if(descriptor) { if(level == SOL_SOCKET) { DescriptorType t = descriptor_getType(descriptor); switch (optname) { case SO_SNDBUF: { if(optlen < sizeof(gint)) { warning("called setsockopt with SO_SNDBUF with optlen < %i", (gint)(sizeof(gint))); errno = EINVAL; result = -1; } else if (t != DT_TCPSOCKET && t != DT_UDPSOCKET) { warning("called setsockopt with SO_SNDBUF on non-socket"); errno = ENOPROTOOPT; result = -1; } else { gint v = *((gint*) optval); socket_setOutputBufferSize((Socket*)descriptor, (gsize)v*2); } break; } case SO_RCVBUF: { if(optlen < sizeof(gint)) { warning("called setsockopt with SO_RCVBUF with optlen < %i", (gint)(sizeof(gint))); errno = EINVAL; result = -1; } else if (t != DT_TCPSOCKET && t != DT_UDPSOCKET) { warning("called setsockopt with SO_RCVBUF on non-socket"); errno = ENOPROTOOPT; result = -1; } else { gint v = *((gint*) optval); socket_setInputBufferSize((Socket*)descriptor, (gsize)v*2); } break; } case SO_REUSEADDR: { // TODO implement this! // XXX Tor actually uses this option!! debug("setsockopt SO_REUSEADDR not yet implemented"); break; } default: { warning("setsockopt optname %i not implemented", optname); errno = ENOSYS; result = -1; break; } } } else { warning("setsockopt level %i not implemented", level); errno = ENOSYS; result = -1; } } else { errno = EBADF; result = -1; } _system_switchOutShadowContext(node); return result; }
gint system_getSockOpt(gint fd, gint level, gint optname, gpointer optval, socklen_t* optlen) { if(!optlen) { errno = EFAULT; return -1; } Host* node = _system_switchInShadowContext(); Descriptor* descriptor = host_lookupDescriptor(node, fd); gint result = 0; /* TODO: implement socket options */ if(descriptor) { if(level == SOL_SOCKET || level == SOL_IP || level == SOL_TCP) { DescriptorType t = descriptor_getType(descriptor); switch (optname) { case TCP_INFO: { if(t == DT_TCPSOCKET) { if(optval) { TCP* tcp = (TCP*)descriptor; tcp_getInfo(tcp, (struct tcp_info *)optval); } *optlen = sizeof(struct tcp_info); result = 0; } else { warning("called getsockopt with TCP_INFO on non-TCP socket"); errno = ENOPROTOOPT; result = -1; } break; } case SO_SNDBUF: { if(*optlen < sizeof(gint)) { warning("called getsockopt with SO_SNDBUF with optlen < %i", (gint)(sizeof(gint))); errno = EINVAL; result = -1; } else if (t != DT_TCPSOCKET && t != DT_UDPSOCKET) { warning("called getsockopt with SO_SNDBUF on non-socket"); errno = ENOPROTOOPT; result = -1; } else { if(optval) { *((gint*) optval) = (gint) socket_getOutputBufferSize((Socket*)descriptor); } *optlen = sizeof(gint); } break; } case SO_RCVBUF: { if(*optlen < sizeof(gint)) { warning("called getsockopt with SO_RCVBUF with optlen < %i", (gint)(sizeof(gint))); errno = EINVAL; result = -1; } else if (t != DT_TCPSOCKET && t != DT_UDPSOCKET) { warning("called getsockopt with SO_RCVBUF on non-socket"); errno = ENOPROTOOPT; result = -1; } else { if(optval) { *((gint*) optval) = (gint) socket_getInputBufferSize((Socket*)descriptor); } *optlen = sizeof(gint); } break; } case SO_ERROR: { if(optval) { *((gint*)optval) = 0; } *optlen = sizeof(gint); result = 0; break; } default: { warning("getsockopt optname %i not implemented", optname); errno = ENOSYS; result = -1; break; } } } else { warning("getsockopt level %i not implemented", level); errno = ENOSYS; result = -1; } } else { errno = EBADF; result = -1; } _system_switchOutShadowContext(node); return result; }
gint host_connectToPeer(Host* host, gint handle, const struct sockaddr* address) { MAGIC_ASSERT(host); sa_family_t family = 0; in_addr_t peerIP = 0; in_port_t peerPort = 0; if(address->sa_family == AF_INET) { struct sockaddr_in* saddr = (struct sockaddr_in*) address; family = saddr->sin_family; peerIP = saddr->sin_addr.s_addr; peerPort = saddr->sin_port; } else if (address->sa_family == AF_UNIX) { struct sockaddr_un* saddr = (struct sockaddr_un*) address; family = saddr->sun_family; gchar* sockpath = saddr->sun_path; peerIP = htonl(INADDR_LOOPBACK); gpointer val = g_hash_table_lookup(host->unixPathToPortMap, sockpath); if(val) { peerPort = (in_port_t)GPOINTER_TO_UINT(val); } } in_addr_t loIP = htonl(INADDR_LOOPBACK); /* make sure we will be able to route this later */ if(peerIP != loIP) { Address* myAddress = networkinterface_getAddress(host->defaultInterface); Address* peerAddress = dns_resolveIPToAddress(worker_getDNS(), peerIP); if(!peerAddress || !topology_isRoutable(worker_getTopology(), myAddress, peerAddress)) { /* can't route it - there is no node with this address */ gchar* peerAddressString = address_ipToNewString(peerIP); warning("attempting to connect to address '%s:%u' for which no host exists", peerAddressString, ntohs(peerPort)); g_free(peerAddressString); return ECONNREFUSED; } } Descriptor* descriptor = host_lookupDescriptor(host, handle); if(descriptor == NULL) { warning("descriptor handle '%i' not found", handle); return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", handle); return EBADF; } DescriptorType type = descriptor_getType(descriptor); if(type != DT_TCPSOCKET && type != DT_UDPSOCKET) { warning("wrong type for descriptor handle '%i'", handle); return ENOTSOCK; } Socket* socket = (Socket*) descriptor; if(!socket_isFamilySupported(socket, family)) { return EAFNOSUPPORT; } if(type == DT_TCPSOCKET) { gint error = tcp_getConnectError((TCP*)socket); if(error) { return error; } } if (address->sa_family == AF_UNIX) { struct sockaddr_un* saddr = (struct sockaddr_un*) address; socket_setUnixPath(socket, saddr->sun_path, FALSE); } if(!socket_isBound(socket)) { /* do an implicit bind to a random port. * use default interface unless the remote peer is on loopback */ in_addr_t defaultIP = networkinterface_getIPAddress(host->defaultInterface); in_addr_t bindAddress = loIP == peerIP ? loIP : defaultIP; in_port_t bindPort = _host_getRandomFreePort(host, bindAddress, type); if(!bindPort) { return EADDRNOTAVAIL; } _host_associateInterface(host, socket, bindAddress, bindPort); } return socket_connectToPeer(socket, peerIP, peerPort, family); }
gint host_bindToInterface(Host* host, gint handle, const struct sockaddr* address) { MAGIC_ASSERT(host); in_addr_t bindAddress = 0; in_port_t bindPort = 0; if(address->sa_family == AF_INET) { struct sockaddr_in* saddr = (struct sockaddr_in*) address; bindAddress = saddr->sin_addr.s_addr; bindPort = saddr->sin_port; } else if (address->sa_family == AF_UNIX) { struct sockaddr_un* saddr = (struct sockaddr_un*) address; /* cant bind twice to the same unix path */ if(g_hash_table_lookup(host->unixPathToPortMap, saddr->sun_path)) { return EADDRINUSE; } bindAddress = htonl(INADDR_LOOPBACK); bindPort = 0; /* choose a random free port below */ } Descriptor* descriptor = host_lookupDescriptor(host, handle); if(descriptor == NULL) { warning("descriptor handle '%i' not found", handle); return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", handle); return EBADF; } DescriptorType type = descriptor_getType(descriptor); if(type != DT_TCPSOCKET && type != DT_UDPSOCKET) { warning("wrong type for descriptor handle '%i'", handle); return ENOTSOCK; } /* make sure we have an interface at that address */ if(!_host_doesInterfaceExist(host, bindAddress)) { return EADDRNOTAVAIL; } Socket* socket = (Socket*) descriptor; /* make sure socket is not bound */ if(socket_isBound(socket)) { warning("socket already bound to requested address"); return EINVAL; } /* make sure we have a proper port */ if(bindPort == 0) { /* we know it will be available */ bindPort = _host_getRandomFreePort(host, bindAddress, type); if(!bindPort) { return EADDRNOTAVAIL; } } else { /* make sure their port is available at that address for this protocol. */ if(!_host_isInterfaceAvailable(host, bindAddress, type, bindPort)) { return EADDRINUSE; } } /* bind port and set associations */ _host_associateInterface(host, socket, bindAddress, bindPort); if (address->sa_family == AF_UNIX) { struct sockaddr_un* saddr = (struct sockaddr_un*) address; gchar* sockpath = g_strndup(saddr->sun_path, 108); /* UNIX_PATH_MAX=108 */ socket_setUnixPath(socket, sockpath, TRUE); g_hash_table_replace(host->unixPathToPortMap, sockpath, GUINT_TO_POINTER(bindPort)); } return 0; }
gboolean host_isShadowDescriptor(Host* host, gint handle) { MAGIC_ASSERT(host); return host_lookupDescriptor(host, handle) == NULL ? FALSE : TRUE; }