Ejemplo n.º 1
0
static void _tcp_endOfFileSignalled(TCP* tcp) {
	MAGIC_ASSERT(tcp);

	debug("%s <-> %s: signaling close to user, socket no longer usable", tcp->super.boundString, tcp->super.peerString);
	tcp->flags |= TCPF_EOF_SIGNALED;

	/* user can no longer access socket */
	descriptor_adjustStatus(&(tcp->super.super.super), DS_CLOSED, TRUE);
	descriptor_adjustStatus(&(tcp->super.super.super), DS_ACTIVE, FALSE);
}
Ejemplo n.º 2
0
gint tcp_acceptServerPeer(TCP* tcp, in_addr_t* ip, in_port_t* port, gint* acceptedHandle) {
	MAGIC_ASSERT(tcp);
	g_assert(acceptedHandle);

	/* make sure we are listening and bound to an ip and port */
	if(tcp->state != TCPS_LISTEN || !(tcp->super.flags & SF_BOUND)) {
		return EINVAL;
	}

	/* we must be a server to accept child connections */
	if(tcp->server == NULL){
		return EINVAL;
	}

	/* if there are no pending connection ready to accept, dont block waiting */
	if(g_queue_get_length(tcp->server->pending) <= 0) {
		return EWOULDBLOCK;
	}

	/* double check the pending child before its accepted */
	TCPChild* child = g_queue_pop_head(tcp->server->pending);
	if(!child || (child->tcp && child->tcp->error == TCPE_CONNECTION_RESET)) {
		return ECONNABORTED;
	}

	MAGIC_ASSERT(child);
	g_assert(child->tcp);

	/* better have a peer if we are established */
	g_assert(child->tcp->super.peerIP && child->tcp->super.peerPort);

	/* child now gets "accepted" */
	child->state = TCPCS_ACCEPTED;

	/* update child descriptor status */
	descriptor_adjustStatus(&(child->tcp->super.super.super), DS_ACTIVE|DS_WRITABLE, TRUE);

	/* update server descriptor status */
	if(g_queue_get_length(tcp->server->pending) > 0) {
		descriptor_adjustStatus(&(tcp->super.super.super), DS_READABLE, TRUE);
	} else {
		descriptor_adjustStatus(&(tcp->super.super.super), DS_READABLE, FALSE);
	}

	*acceptedHandle = child->tcp->super.super.super.handle;
	if(ip) {
		*ip = child->tcp->super.peerIP;
	}
	if(port) {
		*port = child->tcp->super.peerPort;
	}

	return 0;
}
Ejemplo n.º 3
0
Packet* socket_removeFromOutputBuffer(Socket* socket) {
    MAGIC_ASSERT(socket);

    /* see if we have any packets */
    Packet* packet = g_queue_pop_head(socket->outputBuffer);
    if(packet) {
        /* just removed a packet */
        guint length = packet_getPayloadLength(packet);
        socket->outputBufferLength -= length;

        /* check if we need to reduce the buffer size */
        if(socket->outputBufferSizePending > 0) {
            socket_setOutputBufferSize(socket, socket->outputBufferSizePending);
        }

        /* update the tracker input buffer stats */
        Tracker* tracker = host_getTracker(worker_getCurrentHost());
        Descriptor* descriptor = (Descriptor *)socket;
        tracker_updateSocketOutputBuffer(tracker, descriptor->handle, socket->outputBufferLength, socket->outputBufferSize);

        /* we are writable if we now have space */
        if(socket_getOutputBufferSpace(socket) > 0) {
            descriptor_adjustStatus((Descriptor*)socket, DS_WRITABLE, TRUE);
        }
    }

    return packet;
}
Ejemplo n.º 4
0
static gssize channel_receiveUserData(Channel* channel, gpointer buffer, gsize nBytes, in_addr_t* ip, in_port_t* port) {
	MAGIC_ASSERT(channel);
	/* the write end of a unidirectional pipe can not read! */
	g_assert(channel->type != CT_WRITEONLY);

	gsize available = channel->bufferLength;
	if(available == 0) {
		/* we have no data */
		if(!channel_getLinkedChannel(channel)) {
			/* the other end closed (EOF) */
			return 0;
		} else {
			/* blocking on read */
			return -1;
		}
	}

	/* accept some data from the other end of the pipe */
	guint copyLength = (guint) MIN(nBytes, available);
	bytequeue_pop(channel->buffer, buffer, copyLength);
	channel->bufferLength -= copyLength;

	/* we are no longer readable if we have nothing left */
	if(channel->bufferLength <= 0) {
		descriptor_adjustStatus((Descriptor*)channel, DS_READABLE, FALSE);
	}

	return copyLength;
}
Ejemplo n.º 5
0
gboolean socket_addToInputBuffer(Socket* socket, Packet* packet) {
    MAGIC_ASSERT(socket);

    /* check if the packet fits */
    guint length = packet_getPayloadLength(packet);
    if(length > socket_getInputBufferSpace(socket)) {
        return FALSE;
    }

    /* add to our queue */
    g_queue_push_tail(socket->inputBuffer, packet);
    packet_ref(packet);
    socket->inputBufferLength += length;
    packet_addDeliveryStatus(packet, PDS_RCV_SOCKET_BUFFERED);

    /* update the tracker input buffer stats */
    Tracker* tracker = host_getTracker(worker_getCurrentHost());
    Descriptor* descriptor = (Descriptor *)socket;
    tracker_updateSocketInputBuffer(tracker, descriptor->handle, socket->inputBufferLength, socket->inputBufferSize);

    /* we just added a packet, so we are readable */
    if(socket->inputBufferLength > 0) {
        descriptor_adjustStatus((Descriptor*)socket, DS_READABLE, TRUE);
    }

    return TRUE;
}
Ejemplo n.º 6
0
gboolean socket_addToOutputBuffer(Socket* socket, Packet* packet) {
    MAGIC_ASSERT(socket);

    /* check if the packet fits */
    guint length = packet_getPayloadLength(packet);
    if(length > socket_getOutputBufferSpace(socket)) {
        return FALSE;
    }

    /* add to our queue */
    g_queue_push_tail(socket->outputBuffer, packet);
    socket->outputBufferLength += length;
    packet_addDeliveryStatus(packet, PDS_SND_SOCKET_BUFFERED);

    /* update the tracker input buffer stats */
    Tracker* tracker = host_getTracker(worker_getCurrentHost());
    Descriptor* descriptor = (Descriptor *)socket;
    tracker_updateSocketOutputBuffer(tracker, descriptor->handle, socket->outputBufferLength, socket->outputBufferSize);

    /* we just added a packet, we are no longer writable if full */
    if(socket_getOutputBufferSpace(socket) <= 0) {
        descriptor_adjustStatus((Descriptor*)socket, DS_WRITABLE, FALSE);
    }

    /* tell the interface to include us when sending out to the network */
    in_addr_t ip = packet_getSourceIP(packet);
    NetworkInterface* interface = host_lookupInterface(worker_getCurrentHost(), ip);
    networkinterface_wantsSend(interface, socket);

    return TRUE;
}
Ejemplo n.º 7
0
Channel* channel_new(gint handle, gint linkedHandle, enum ChannelType type) {
	Channel* channel = g_new0(Channel, 1);
	MAGIC_INIT(channel);

	transport_init(&(channel->super), &channel_functions, DT_PIPE, handle);

	channel->type = type;
	channel->buffer = bytequeue_new(8192);
	channel->bufferSize = CONFIG_PIPE_BUFFER_SIZE;
	channel->linkedHandle = linkedHandle;

	descriptor_adjustStatus((Descriptor*)channel, DS_ACTIVE, TRUE);
	if(!(type & CT_READONLY)) {
		descriptor_adjustStatus((Descriptor*)channel, DS_WRITABLE, TRUE);
	}

	return channel;
}
Ejemplo n.º 8
0
static gsize _tcp_getBufferSpaceOut(TCP* tcp) {
	MAGIC_ASSERT(tcp);
	/* account for throttled and retransmission buffer */
	gssize s = (gssize)(socket_getOutputBufferSpace(&(tcp->super)) - tcp->throttledOutputLength - tcp->retransmissionLength);
	gsize space = MAX(0, s);
	if(space == 0) {
		descriptor_adjustStatus((Descriptor*)tcp, DS_WRITABLE, FALSE);
	}
	return space;
}
Ejemplo n.º 9
0
UDP* udp_new(gint handle, guint receiveBufferSize, guint sendBufferSize) {
	UDP* udp = g_new0(UDP, 1);
	MAGIC_INIT(udp);

	socket_init(&(udp->super), &udp_functions, DT_UDPSOCKET, handle, receiveBufferSize, sendBufferSize);

	/* we are immediately active because UDP doesnt wait for accept or connect */
	descriptor_adjustStatus((Descriptor*) udp, DS_ACTIVE|DS_WRITABLE, TRUE);

	return udp;
}
Ejemplo n.º 10
0
static gssize channel_sendUserData(Channel* channel, gconstpointer buffer, gsize nBytes, in_addr_t ip, in_port_t port) {
	MAGIC_ASSERT(channel);
	/* the read end of a unidirectional pipe can not write! */
	g_assert(channel->type != CT_READONLY);

	gint result = 0;

	Channel* linkedChannel = channel_getLinkedChannel(channel);
	if(linkedChannel) {
		result = channel_linkedWrite(linkedChannel, buffer, nBytes);
	}

	/* our end cant write anymore if they returned error */
	if(result <= 0) {
		descriptor_adjustStatus((Descriptor*)channel, DS_WRITABLE, FALSE);
	}

	return result;
}
Ejemplo n.º 11
0
static gssize channel_linkedWrite(Channel* channel, gconstpointer buffer, gsize nBytes) {
	MAGIC_ASSERT(channel);
	/* our linked channel is trying to send us data, make sure we can read it */
	g_assert(!(channel->type & CT_WRITEONLY));

	gsize available = channel->bufferSize - channel->bufferLength;
	if(available == 0) {
		/* we have no space */
		return -1;
	}

	/* accept some data from the other end of the pipe */
	guint copyLength = (guint) MIN(nBytes, available);
	bytequeue_push(channel->buffer, buffer, copyLength);
	channel->bufferLength += copyLength;

	/* we just got some data in our buffer */
	descriptor_adjustStatus((Descriptor*)channel, DS_READABLE, TRUE);

	return copyLength;
}
Ejemplo n.º 12
0
/* return TRUE if the packet should be retransmitted */
gboolean tcp_processPacket(TCP* tcp, Packet* packet) {
	MAGIC_ASSERT(tcp);

	/* fetch the TCP info from the packet */
	PacketTCPHeader header;
	packet_getTCPHeader(packet, &header);
	guint packetLength = packet_getPayloadLength(packet);

	/* if we run a server, the packet could be for an existing child */
	tcp = _tcp_getSourceTCP(tcp, header.sourceIP, header.sourcePort);

	/* now we have the true TCP for the packet */
	MAGIC_ASSERT(tcp);

	/* print packet info for debugging */
	debug("%s <-> %s: processing packet# %u length %u",
			tcp->super.boundString, tcp->super.peerString, header.sequence, packetLength);

	/* if packet is reset, don't process */
	if(header.flags & PTCP_RST) {
		/* @todo: not sure if this is handled correctly */
		debug("received RESET packet");

		if(!(tcp->state & TCPS_LISTEN) && !(tcp->error & TCPE_CONNECTION_RESET)) {
			tcp->error |= TCPE_CONNECTION_RESET;
			tcp->flags |= TCPF_REMOTE_CLOSED;

			_tcp_setState(tcp, TCPS_TIMEWAIT);

			/* it will send no more user data after what we have now */
			tcp->receive.end = tcp->receive.next;
		}

		packet_unref(packet);
		return FALSE;
	}

	/* if we are a server, we have to remember who we got this from so we can
	 * respond back to them. this is because we could be bound to several
	 * interfaces and otherwise cant decide which to send on.
	 */
	if(tcp->server) {
		tcp->server->lastPeerIP = header.sourceIP;
		tcp->server->lastPeerPort = header.sourcePort;
		tcp->server->lastIP = header.destinationIP;
	}

	/* go through the state machine, tracking processing and response */
	gboolean wasProcessed = FALSE;
	enum ProtocolTCPFlags responseFlags = PTCP_NONE;

	switch(tcp->state) {
		case TCPS_LISTEN: {
			/* receive SYN, send SYNACK, move to SYNRECEIVED */
			if(header.flags & PTCP_SYN) {
				MAGIC_ASSERT(tcp->server);
				wasProcessed = TRUE;

				/* we need to multiplex a new child */
				Node* node = worker_getPrivate()->cached_node;
				gint multiplexedHandle = node_createDescriptor(node, DT_TCPSOCKET);
				TCP* multiplexed = (TCP*) node_lookupDescriptor(node, multiplexedHandle);

				multiplexed->child = _tcpchild_new(multiplexed, tcp, header.sourceIP, header.sourcePort);
				g_assert(g_hash_table_lookup(tcp->server->children, &(multiplexed->child->key)) == NULL);
				g_hash_table_replace(tcp->server->children, &(multiplexed->child->key), multiplexed->child);

				multiplexed->receive.start = header.sequence;
				multiplexed->receive.next = multiplexed->receive.start + 1;

				debug("%s <-> %s: server multiplexed child socket %s <-> %s",
						tcp->super.boundString, tcp->super.peerString,
						multiplexed->super.boundString, multiplexed->super.peerString);

				_tcp_setState(multiplexed, TCPS_SYNRECEIVED);

				/* parent will send response */
				responseFlags = PTCP_SYN|PTCP_ACK;
			}
			break;
		}

		case TCPS_SYNSENT: {
			/* receive SYNACK, send ACK, move to ESTABLISHED */
			if((header.flags & PTCP_SYN) && (header.flags & PTCP_ACK)) {
				wasProcessed = TRUE;
				tcp->receive.start = header.sequence;
				tcp->receive.next = tcp->receive.start + 1;

				responseFlags |= PTCP_ACK;
				_tcp_setState(tcp, TCPS_ESTABLISHED);
			}
			/* receive SYN, send ACK, move to SYNRECEIVED (simultaneous open) */
			else if(header.flags & PTCP_SYN) {
				wasProcessed = TRUE;
				tcp->receive.start = header.sequence;
				tcp->receive.next = tcp->receive.start + 1;

				responseFlags |= PTCP_ACK;
				_tcp_setState(tcp, TCPS_SYNRECEIVED);
			}

			break;
		}

		case TCPS_SYNRECEIVED: {
			/* receive ACK, move to ESTABLISHED */
			if(header.flags & PTCP_ACK) {
				wasProcessed = TRUE;
				_tcp_setState(tcp, TCPS_ESTABLISHED);

				/* if this is a child, mark it accordingly */
				if(tcp->child) {
					tcp->child->state = TCPCS_PENDING;
					g_queue_push_tail(tcp->child->parent->server->pending, tcp->child);
					/* user should accept new child from parent */
					descriptor_adjustStatus(&(tcp->child->parent->super.super.super), DS_READABLE, TRUE);
				}
			}
			break;
		}

		case TCPS_ESTABLISHED: {
			/* receive FIN, send FINACK, move to CLOSEWAIT */
			if(header.flags & PTCP_FIN) {
				wasProcessed = TRUE;

				/* other side of connections closed */
				tcp->flags |= TCPF_REMOTE_CLOSED;
				responseFlags |= (PTCP_FIN|PTCP_ACK);
				_tcp_setState(tcp, TCPS_CLOSEWAIT);

				/* remote will send us no more user data after this sequence */
				tcp->receive.end = header.sequence;
			}
			break;
		}

		case TCPS_FINWAIT1: {
			/* receive FINACK, move to FINWAIT2 */
			if((header.flags & PTCP_FIN) && (header.flags & PTCP_ACK)) {
				wasProcessed = TRUE;
				_tcp_setState(tcp, TCPS_FINWAIT2);
			}
			/* receive FIN, send FINACK, move to CLOSING (simultaneous close) */
			else if(header.flags & PTCP_FIN) {
				wasProcessed = TRUE;
				responseFlags |= (PTCP_FIN|PTCP_ACK);
				tcp->flags |= TCPF_REMOTE_CLOSED;
				_tcp_setState(tcp, TCPS_CLOSING);

				/* it will send no more user data after this sequence */
				tcp->receive.end = header.sequence;
			}
			break;
		}

		case TCPS_FINWAIT2: {
			/* receive FIN, send FINACK, move to TIMEWAIT */
			if(header.flags & PTCP_FIN) {
				wasProcessed = TRUE;
				responseFlags |= (PTCP_FIN|PTCP_ACK);
				tcp->flags |= TCPF_REMOTE_CLOSED;
				_tcp_setState(tcp, TCPS_TIMEWAIT);

				/* it will send no more user data after this sequence */
				tcp->receive.end = header.sequence;
			}
			break;
		}

		case TCPS_CLOSING: {
			/* receive FINACK, move to TIMEWAIT */
			if((header.flags & PTCP_FIN) && (header.flags & PTCP_ACK)) {
				wasProcessed = TRUE;
				_tcp_setState(tcp, TCPS_TIMEWAIT);
			}
			break;
		}

		case TCPS_TIMEWAIT: {
			break;
		}

		case TCPS_CLOSEWAIT: {
			break;
		}

		case TCPS_LASTACK: {
			/* receive FINACK, move to CLOSED */
			if((header.flags & PTCP_FIN) && (header.flags & PTCP_ACK)) {
				wasProcessed = TRUE;
				_tcp_setState(tcp, TCPS_CLOSED);
				/* we closed, cant use tcp anymore, no retransmit */
				packet_unref(packet);
				return FALSE;
			}
			break;
		}

		case TCPS_CLOSED: {
			/* stray packet, drop without retransmit */
			packet_unref(packet);
			return FALSE;
			break;
		}

		default: {
			break;
		}

	}

	gint nPacketsAcked = 0;

	/* check if we can update some TCP control info */
	if(header.flags & PTCP_ACK) {
		wasProcessed = TRUE;
		if((header.acknowledgement > tcp->send.unacked) && (header.acknowledgement <= tcp->send.next)) {
			/* some data we sent got acknowledged */
			nPacketsAcked = header.acknowledgement - tcp->send.unacked;

			/* the packets just acked are 'released' from retransmit queue */
			for(guint i = tcp->send.unacked; i < header.acknowledgement; i++) {
				_tcp_removeRetransmit(tcp, i);
			}

			tcp->send.unacked = header.acknowledgement;

			/* update congestion window and keep track of when it was updated */
			tcp->congestion.lastWindow = header.window;
			tcp->congestion.lastSequence = header.sequence;
			tcp->congestion.lastAcknowledgement = header.acknowledgement;
		}
	}

	gboolean doRetransmitData = FALSE;

	/* check if the packet carries user data for us */
	if(packetLength > 0) {
		/* it has data, check if its in the correct range */
		if(header.sequence >= (tcp->receive.next + tcp->receive.window)) {
			/* its too far ahead to accept now, but they should re-send it */
			wasProcessed = TRUE;
			doRetransmitData = TRUE;

		} else if(header.sequence >= tcp->receive.next) {
			/* its in our window, so we can accept the data */
			wasProcessed = TRUE;

			/*
			 * if this is THE next packet, we MUST accept it to avoid
			 * deadlocks (unless we are blocked b/c user should read)
			 */
			gboolean isNextPacket = (header.sequence == tcp->receive.next) ? TRUE : FALSE;
			gboolean waitingUserRead = (socket_getInputBufferSpace(&(tcp->super)) > 0) ? TRUE : FALSE;
			gboolean packetFits = (packetLength <= _tcp_getBufferSpaceIn(tcp)) ? TRUE : FALSE;

			if((isNextPacket && !waitingUserRead) || (packetFits)) {
				/* make sure its in order */
				_tcp_bufferPacketIn(tcp, packet);
			} else {
				debug("no space for packet even though its in our window");
				doRetransmitData = TRUE;
			}
		}
	}

	/* if it is a spurious packet, send a reset */
	if(!wasProcessed) {
		g_assert(responseFlags == PTCP_NONE);
		responseFlags = PTCP_RST;
	}

	/* try to update congestion window based on potentially new info */
	_tcp_updateCongestionWindow(tcp, nPacketsAcked);

	/* now flush as many packets as we can to socket */
	_tcp_flush(tcp);

	/* send ack if they need updates but we didn't send any yet (selective acks) */
	if((tcp->receive.next > tcp->send.lastAcknowledgement) ||
			(tcp->receive.window != tcp->send.lastWindow))
	{
		responseFlags |= PTCP_ACK;
	}

	/* send control packet if we have one */
	if(responseFlags != PTCP_NONE) {
		debug("%s <-> %s: sending response control packet",
				tcp->super.boundString, tcp->super.peerString);
		Packet* response = _tcp_createPacket(tcp, responseFlags, NULL, 0);
		_tcp_bufferPacketOut(tcp, response);
		_tcp_flush(tcp);
	}

	/* we should free packets that are done but were not buffered */
	if(!doRetransmitData && packetLength <= 0) {
		packet_unref(packet);
	}
	return doRetransmitData;
}
Ejemplo n.º 13
0
void descriptor_close(Descriptor* descriptor) {
    MAGIC_ASSERT(descriptor);
    MAGIC_ASSERT(descriptor->funcTable);
    descriptor_adjustStatus(descriptor, DS_CLOSED, TRUE);
    descriptor->funcTable->close(descriptor);
}
Ejemplo n.º 14
0
static void _tcp_flush(TCP* tcp) {
	MAGIC_ASSERT(tcp);

	/* make sure our information is up to date */
	_tcp_updateReceiveWindow(tcp);
	_tcp_updateSendWindow(tcp);

	/* flush packets that can now be sent to socket */
	while(g_queue_get_length(tcp->throttledOutput) > 0) {
		/* get the next throttled packet, in sequence order */
		Packet* packet = g_queue_pop_head(tcp->throttledOutput);

		/* break out if we have no packets left */
		if(!packet) {
			break;
		}

		guint length = packet_getPayloadLength(packet);

		if(length > 0) {
			PacketTCPHeader header;
			packet_getTCPHeader(packet, &header);

			/* we cant send it if our window is too small */
			gboolean fitsInWindow = (header.sequence < (tcp->send.unacked + tcp->send.window)) ? TRUE : FALSE;

			/* we cant send it if we dont have enough space */
			gboolean fitsInBuffer = (length <= socket_getOutputBufferSpace(&(tcp->super))) ? TRUE : FALSE;

			if(!fitsInBuffer || !fitsInWindow) {
				/* we cant send the packet yet */
				g_queue_push_head(tcp->throttledOutput, packet);
				break;
			} else {
				/* we will send: store length in virtual retransmission buffer
				 * so we can reduce buffer space consumed when we receive the ack */
				_tcp_addRetransmit(tcp, header.sequence, length);
			}
		}

		/* packet is sendable, we removed it from out buffer */
		tcp->throttledOutputLength -= length;

		/* update TCP header to our current advertised window and acknowledgement */
		packet_updateTCP(packet, tcp->receive.next, tcp->receive.window);

		/* keep track of the last things we sent them */
		tcp->send.lastAcknowledgement = tcp->receive.next;
		tcp->send.lastWindow = tcp->receive.window;

		 /* socket will queue it ASAP */
		gboolean success = socket_addToOutputBuffer(&(tcp->super), packet);

		/* we already checked for space, so this should always succeed */
		g_assert(success);
	}

	/* any packets now in order can be pushed to our user input buffer */
	while(g_queue_get_length(tcp->unorderedInput) > 0) {
		Packet* packet = g_queue_pop_head(tcp->unorderedInput);

		PacketTCPHeader header;
		packet_getTCPHeader(packet, &header);

		if(header.sequence == tcp->receive.next) {
			/* move from the unordered buffer to user input buffer */
			gboolean fitInBuffer = socket_addToInputBuffer(&(tcp->super), packet);

			if(fitInBuffer) {
				tcp->unorderedInputLength -= packet_getPayloadLength(packet);
				(tcp->receive.next)++;
				continue;
			}
		}

		/* we could not buffer it because its out of order or we have no space */
		g_queue_push_head(tcp->unorderedInput, packet);
		break;
	}

	/* check if user needs an EOF signal */
	gboolean wantsEOF = ((tcp->flags & TCPF_LOCAL_CLOSED) || (tcp->flags & TCPF_REMOTE_CLOSED)) ? TRUE : FALSE;
	if(wantsEOF) {
		/* if anyone closed, can't send anymore */
		tcp->error |= TCPE_SEND_EOF;

		if((tcp->receive.next >= tcp->receive.end) && !(tcp->flags & TCPF_EOF_SIGNALED)) {
			/* user needs to read a 0 so it knows we closed */
			tcp->error |= TCPE_RECEIVE_EOF;
			descriptor_adjustStatus((Descriptor*)tcp, DS_READABLE, TRUE);
		}
	}
}
Ejemplo n.º 15
0
static void _tcp_setState(TCP* tcp, enum TCPState state) {
	MAGIC_ASSERT(tcp);

	tcp->stateLast = tcp->state;
	tcp->state = state;

	debug("%s <-> %s: moved from TCP state '%s' to '%s'", tcp->super.boundString, tcp->super.peerString,
			tcp_stateToAscii(tcp->stateLast), tcp_stateToAscii(tcp->state));

	/* some state transitions require us to update the descriptor status */
	switch (state) {
		case TCPS_LISTEN: {
			descriptor_adjustStatus((Descriptor*)tcp, DS_ACTIVE, TRUE);
			break;
		}
		case TCPS_SYNSENT: {
			break;
		}
		case TCPS_SYNRECEIVED: {
			break;
		}
		case TCPS_ESTABLISHED: {
			tcp->flags |= TCPF_WAS_ESTABLISHED;
			if(tcp->state != tcp->stateLast) {
				_tcp_autotune(tcp);
			}
			descriptor_adjustStatus((Descriptor*)tcp, DS_ACTIVE|DS_WRITABLE, TRUE);
			break;
		}
		case TCPS_CLOSING: {
			break;
		}
		case TCPS_CLOSEWAIT: {
			break;
		}
		case TCPS_CLOSED: {
			/* user can no longer use socket */
			descriptor_adjustStatus((Descriptor*)tcp, DS_ACTIVE, FALSE);

			/*
			 * servers have to wait for all children to close.
			 * children need to notify their parents when closing.
			 */
			if(!tcp->server || g_hash_table_size(tcp->server->children) <= 0) {
				if(tcp->child && tcp->child->parent) {
					TCP* parent = tcp->child->parent;

					/* tell my server to stop accepting packets for me
					 * this will destroy the child and NULL out tcp->child */
					g_hash_table_remove(tcp->child->parent->server->children, (gconstpointer)&(tcp->child->key));

					/* if i was the server's last child and its waiting to close, close it */
					g_assert(parent->server);
					if((parent->state == TCPS_CLOSED) && (g_hash_table_size(parent->server->children) <= 0)) {
						/* this will unbind from the network interface and free socket */
						node_closeDescriptor(worker_getPrivate()->cached_node, parent->super.super.super.handle);
					}
				}

				/* this will unbind from the network interface and free socket */
				node_closeDescriptor(worker_getPrivate()->cached_node, tcp->super.super.super.handle);
			}
			break;
		}
		case TCPS_TIMEWAIT: {
			/* schedule a close timer self-event to finish out the closing process */
			TCPCloseTimerExpiredEvent* event = tcpclosetimerexpired_new(tcp);
			worker_scheduleEvent((Event*)event, CONFIG_TCPCLOSETIMER_DELAY, 0);
			break;
		}
		default:
			break;
	}
}
Ejemplo n.º 16
0
gssize tcp_receiveUserData(TCP* tcp, gpointer buffer, gsize nBytes, in_addr_t* ip, in_port_t* port) {
	MAGIC_ASSERT(tcp);

	/*
	 * TODO
	 * We call descriptor_adjustStatus too many times here, to handle the readable
	 * state of the socket at times when we have a partially read packet. Consider
	 * adding a required hook for socket subclasses so the socket layer can
	 * query TCP for readability status.
	 */

	/* make sure we pull in all readable user data */
	_tcp_flush(tcp);

	gsize remaining = nBytes;
	gsize bytesCopied = 0;
	gsize totalCopied = 0;
	gsize offset = 0;
	gsize copyLength = 0;

	/* check if we have a partial packet waiting to get finished */
	if(remaining > 0 && tcp->partialUserDataPacket) {
		guint partialLength = packet_getPayloadLength(tcp->partialUserDataPacket);
		guint partialBytes = partialLength - tcp->partialOffset;
		g_assert(partialBytes > 0);

		copyLength = MIN(partialBytes, remaining);
		bytesCopied = packet_copyPayload(tcp->partialUserDataPacket, tcp->partialOffset, buffer, copyLength);
		totalCopied += bytesCopied;
		remaining -= bytesCopied;
		offset += bytesCopied;

		if(bytesCopied >= partialBytes) {
			/* we finished off the partial packet */
			packet_unref(tcp->partialUserDataPacket);
			tcp->partialUserDataPacket = NULL;
			tcp->partialOffset = 0;
		} else {
			/* still more partial bytes left */
			tcp->partialOffset += bytesCopied;
			g_assert(remaining == 0);
		}
	}

	while(remaining > 0) {
		/* if we get here, we should have read the partial packet above, or
		 * broken out below */
		g_assert(tcp->partialUserDataPacket == NULL);
		g_assert(tcp->partialOffset == 0);

		/* get the next buffered packet - we'll always need it.
		 * this could mark the socket as unreadable if this is its last packet.*/
		Packet* packet = socket_removeFromInputBuffer((Socket*)tcp);
		if(!packet) {
			/* no more packets or partial packets */
			break;
		}

		guint packetLength = packet_getPayloadLength(packet);
		copyLength = MIN(packetLength, remaining);
		bytesCopied = packet_copyPayload(packet, 0, buffer + offset, copyLength);
		totalCopied += bytesCopied;
		remaining -= bytesCopied;
		offset += bytesCopied;

		if(bytesCopied < packetLength) {
			/* we were only able to read part of this packet */
			tcp->partialUserDataPacket = packet;
			tcp->partialOffset = bytesCopied;
			break;
		}

		/* we read the entire packet, and are now finished with it */
		packet_unref(packet);
	}

	/* now we update readability of the socket */
	if((tcp->super.inputBufferLength > 0) || (tcp->partialUserDataPacket != NULL)) {
		/* we still have readable data */
		descriptor_adjustStatus(&(tcp->super.super.super), DS_READABLE, TRUE);
	} else {
		/* all of our ordered user data has been read */
		if((tcp->unorderedInputLength == 0) && (tcp->error & TCPE_RECEIVE_EOF)) {
			/* there is no more unordered data either, and we need to signal EOF */
			if(totalCopied > 0) {
				/* we just received bytes, so we can't EOF until the next call.
				 * make sure we stay readable so we DO actually EOF the socket */
				descriptor_adjustStatus(&(tcp->super.super.super), DS_READABLE, TRUE);
			} else {
				/* OK, no more data and nothing just received. */
				if(tcp->flags & TCPF_EOF_SIGNALED) {
					/* we already signaled close, now its an error */
					return -2;
				} else {
					/* we have not signaled close, do that now and close out the socket */
					_tcp_endOfFileSignalled(tcp);
					return 0;
				}
			}
		} else {
			/* our socket still has unordered data or is still open, but empty for now */
			descriptor_adjustStatus(&(tcp->super.super.super), DS_READABLE, FALSE);
		}
	}

	debug("%s <-> %s: receiving %lu user bytes", tcp->super.boundString, tcp->super.peerString, totalCopied);

	return (gssize) (totalCopied == 0 ? -1 : totalCopied);
}
Ejemplo n.º 17
0
static void _epoll_close(Epoll* epoll) {
	MAGIC_ASSERT(epoll);
	descriptor_adjustStatus(&(epoll->super), DS_CLOSED, TRUE);
	node_closeDescriptor(worker_getPrivate()->cached_node, epoll->super.handle);
}
Ejemplo n.º 18
0
static void channel_close(Channel* channel) {
	MAGIC_ASSERT(channel);
	descriptor_adjustStatus((Descriptor*)channel, DS_CLOSED, TRUE);
	node_closeDescriptor(worker_getPrivate()->cached_node, channel->super.super.handle);
}