diff --git a/udprecvqueue.cpp b/udprecvqueue.cpp
new file mode 100644
index 0000000..0300428
--- /dev/null
+++ b/udprecvqueue.cpp
@@ -0,0 +1,95 @@
+
+#include <cstring>
+
+#include "tunnel.h"
+#include "connection.h"
+#include "udprecvqueue.h"
+
+
+
+UdpRecvQueue::UdpRecvQueue(Connection &connection):
+	connection(connection),
+	nextConfirmSendTime(connection.tunnel.time + connection.tunnel.udpConfirmDuration),
+	confirmRequired(),
+	finalEntryAdded(),
+	entries(),
+	currentIndex(),
+	endIndex()
+	{ }
+
+
+UdpRecvQueue::~UdpRecvQueue()
+	{ }
+
+
+bool UdpRecvQueue::recvUdp(unsigned int index, const void *data, int size) {
+	confirmRequired = true;
+	
+	if (size < 0 || size > PACKET_SIZE)
+		return false;
+	if (cycleLess(index, currentIndex) || !cycleLess(index, currentIndex + PACKETS_COUNT))
+		return false;
+	if (finalEntryAdded && !cycleLess(index, endIndex))
+		return false;
+	Entry &e = entries[(current + (index - currentIndex))%PACKETS_COUNT];
+	if (e.received)
+		return false;
+	
+	e.received = true;
+	e.size = size;
+	if (e.size) {
+		memcpy(e.data, data, e.size);
+		if (cycleLess(endIndex, index + 1)) endIndex = index + 1;
+	} else {
+		finalEntryAdded = true;
+		endIndex = index + 1;
+	}
+	
+	return true;
+}
+
+
+bool UdpRecvQueue::sendUdpConfirm() {
+	Time time = connection.tunnel.time;
+	if (!confirmRequired || timeLess(time, nextConfirmSendTime))
+		return false;
+	
+	unsigned int count = endIndex - currentIndex;
+	unsigned int bytesCount = count ? (count - 1)/8 + 1 : 0;
+	
+	Packet packet;
+	packet.init(connection.id.id, currentIndex, CMD_CONFIRM, bytesCount);
+	for(int i = 0; i < count; ++i)
+		if (entries[(current + i)%PACKETS_COUNT].received)
+			packet.payload[i/8] |= (1 << (i%8));
+	connection.tunnel.sendUdpPacket(packet);
+	
+	confirmRequired = false;
+	nextConfirmSendTime = time + connection.tunnel.udpConfirmDuration;
+	return true;
+}
+
+
+int UdpRecvQueue::sendTcp() {
+	int sent = 0;
+	while(cycleLess(currentIndex, endIndex)) {
+		Entry &e = entries[current];
+		if (!e.received)
+			break;
+
+		if (!e.size) { 
+			connection.udpActive = false;
+			connection.tunnel.termQueue.insert(connection.id);
+		} else
+		if (!connection.tcpSendQueue.push(e.data, e.size)) {
+			break;
+		}
+		
+		++sent;
+		memset(&e, 0, sizeof(e));
+		current = (current + 1)%PACKETS_COUNT;
+	}
+	if (sent) confirmRequired = true;
+	return sent;
+}
+
diff --git a/udprecvqueue.h b/udprecvqueue.h
new file mode 100644
index 0000000..f244b8b
--- /dev/null
+++ b/udprecvqueue.h
@@ -0,0 +1,42 @@
+#ifndef UDPRECVQUEUE_H
+#define UDPRECVQUEUE_H
+
+
+#include "common.h"
+
+
+
+class Connection;
+
+
+class UdpRecvQueue {
+public:
+	struct Entry {
+		bool received;
+		unsigned short size;
+		unsigned char data[PACKET_SIZE];
+	};
+	
+public:
+	Connection &connection;
+private:
+	Time nextConfirmSendTime;
+	bool confirmRequired;
+	bool finalEntryAdded;
+	
+	Entry entries[PACKETS_COUNT];
+	int current;
+	unsigned int currentIndex;
+	unsigned int endIndex;
+	
+public:
+	explicit UdpRecvQueue(Connection &connection);
+	~UdpRecvQueue();
+	
+	bool recvUdp(unsigned int index, const void *data, int size);
+	bool sendUdpConfirm();
+	int sendTcp();
+};
+
+
+#endif