gsize socket_getOutputBufferSpace(Socket* socket) { MAGIC_ASSERT(socket); utility_assert(socket->outputBufferSize >= socket->outputBufferLength); gsize bufferSize = socket_getOutputBufferSize(socket); if(bufferSize < socket->outputBufferLength) { return 0; } else { return bufferSize - socket->outputBufferLength; } }
/* * this function builds a UDP packet and sends to the virtual node given by the * ip and port parameters. this function assumes that the socket is already * bound to a local port, no matter if that happened explicitly or implicitly. */ gssize udp_sendUserData(UDP* udp, gconstpointer buffer, gsize nBytes, in_addr_t ip, in_port_t port) { MAGIC_ASSERT(udp); gsize space = socket_getOutputBufferSpace(&(udp->super)); if(space < nBytes) { /* not enough space to buffer the data */ return -1; } /* break data into segments and send each in a packet */ gsize maxPacketLength = CONFIG_DATAGRAM_MAX_SIZE; gsize remaining = nBytes; gsize offset = 0; /* create as many packets as needed */ while(remaining > 0) { gsize copyLength = MIN(maxPacketLength, remaining); /* use default destination if none was specified */ in_addr_t destinationIP = (ip != 0) ? ip : udp->super.peerIP; in_port_t destinationPort = (port != 0) ? port : udp->super.peerPort; /* create the UDP packet */ Packet* packet = packet_new(buffer + offset, copyLength); packet_setUDP(packet, PUDP_NONE, socket_getBinding(&(udp->super)), udp->super.boundPort, destinationIP, destinationPort); /* buffer it in the transport layer, to be sent out when possible */ gboolean success = socket_addToOutputBuffer((Socket*) udp, packet); /* counter maintenance */ if(success) { remaining -= copyLength; offset += copyLength; } else { warning("unable to send UDP packet"); break; } } /* update the tracker output buffer stats */ Tracker* tracker = node_getTracker(worker_getPrivate()->cached_node); Socket* socket = (Socket* )udp; Descriptor* descriptor = (Descriptor *)socket; gsize outLength = socket_getOutputBufferLength(socket); gsize outSize = socket_getOutputBufferSize(socket); tracker_updateSocketOutputBuffer(tracker, descriptor->handle, outLength, outSize); debug("buffered %"G_GSIZE_FORMAT" outbound UDP bytes from user", offset); return (gssize) offset; }
gssize udp_receiveUserData(UDP* udp, gpointer buffer, gsize nBytes, in_addr_t* ip, in_port_t* port) { MAGIC_ASSERT(udp); Packet* packet = socket_removeFromInputBuffer((Socket*)udp); if(!packet) { return -1; } /* copy lesser of requested and available amount to application buffer */ guint packetLength = packet_getPayloadLength(packet); gsize copyLength = MIN(nBytes, packetLength); guint bytesCopied = packet_copyPayload(packet, 0, buffer, copyLength); utility_assert(bytesCopied == copyLength); packet_addDeliveryStatus(packet, PDS_RCV_SOCKET_DELIVERED); /* fill in address info */ if(ip) { *ip = packet_getSourceIP(packet); } if(port) { *port = packet_getSourcePort(packet); } /* destroy packet, throwing away any bytes not claimed by the app */ packet_unref(packet); /* update the tracker output buffer stats */ Tracker* tracker = host_getTracker(worker_getCurrentHost()); Socket* socket = (Socket* )udp; Descriptor* descriptor = (Descriptor *)socket; gsize outLength = socket_getOutputBufferLength(socket); gsize outSize = socket_getOutputBufferSize(socket); tracker_updateSocketOutputBuffer(tracker, descriptor->handle, outLength, outSize); debug("user read %u inbound UDP bytes", bytesCopied); return (gssize)bytesCopied; }
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; }
/* * this function builds a UDP packet and sends to the virtual node given by the * ip and port parameters. this function assumes that the socket is already * bound to a local port, no matter if that happened explicitly or implicitly. */ gssize udp_sendUserData(UDP* udp, gconstpointer buffer, gsize nBytes, in_addr_t ip, in_port_t port) { MAGIC_ASSERT(udp); gsize space = socket_getOutputBufferSpace(&(udp->super)); if(space < nBytes) { /* not enough space to buffer the data */ return -1; } /* break data into segments and send each in a packet */ gsize maxPacketLength = CONFIG_DATAGRAM_MAX_SIZE; gsize remaining = nBytes; gsize offset = 0; /* create as many packets as needed */ while(remaining > 0) { gsize copyLength = MIN(maxPacketLength, remaining); /* use default destination if none was specified */ in_addr_t destinationIP = (ip != 0) ? ip : udp->super.peerIP; in_port_t destinationPort = (port != 0) ? port : udp->super.peerPort; in_addr_t sourceIP = 0; in_port_t sourcePort = 0; socket_getSocketName(&(udp->super), &sourceIP, &sourcePort); if(sourceIP == htonl(INADDR_ANY)) { /* source interface depends on destination */ if(destinationIP == htonl(INADDR_LOOPBACK)) { sourceIP = htonl(INADDR_LOOPBACK); } else { sourceIP = host_getDefaultIP(worker_getCurrentHost()); } } utility_assert(sourceIP && sourcePort && destinationIP && destinationPort); /* create the UDP packet */ Packet* packet = packet_new(buffer + offset, copyLength); packet_setUDP(packet, PUDP_NONE, sourceIP, sourcePort, destinationIP, destinationPort); packet_addDeliveryStatus(packet, PDS_SND_CREATED); /* buffer it in the transport layer, to be sent out when possible */ gboolean success = socket_addToOutputBuffer((Socket*) udp, packet); /* counter maintenance */ if(success) { remaining -= copyLength; offset += copyLength; } else { warning("unable to send UDP packet"); break; } } /* update the tracker output buffer stats */ Tracker* tracker = host_getTracker(worker_getCurrentHost()); Socket* socket = (Socket* )udp; Descriptor* descriptor = (Descriptor *)socket; gsize outLength = socket_getOutputBufferLength(socket); gsize outSize = socket_getOutputBufferSize(socket); tracker_updateSocketOutputBuffer(tracker, descriptor->handle, outLength, outSize); debug("buffered %"G_GSIZE_FORMAT" outbound UDP bytes from user", offset); return (gssize) offset; }