2020-03-27 18:06:16 +00:00
|
|
|
//#include "IPV4"
|
2020-08-07 07:39:07 +01:00
|
|
|
//#include "ICMP" // this is wrong and only doing this because we're just in dev right now. probably need approach like Shrine, MakeNet, idk.
|
|
|
|
//#include "Sockets"
|
2020-03-27 18:06:16 +00:00
|
|
|
|
|
|
|
#define UDP_MAX_PORT 65535
|
|
|
|
|
|
|
|
class CUDPHeader
|
|
|
|
{
|
|
|
|
U16 source_port;
|
|
|
|
U16 destination_port;
|
|
|
|
U16 length;
|
|
|
|
U16 checksum;
|
|
|
|
};
|
|
|
|
|
|
|
|
class CUDPSocket
|
|
|
|
{
|
2020-07-31 18:49:37 +01:00
|
|
|
CSocket *socket;
|
|
|
|
I64 receive_timeout_ms;
|
|
|
|
I64 receive_max_timeout;
|
|
|
|
U8 *receive_buffer;
|
|
|
|
I64 receive_len;
|
|
|
|
CSocketAddressStorage receive_address; // based on ->family, cast or assign to a var as IPV4/IPV6 CSocketAddress
|
|
|
|
U16 bound_to; // represents the currently bound port
|
2020-08-07 07:39:07 +01:00
|
|
|
|
|
|
|
CSocketAddressStorage from_address; // when UDP Handler sees UDP packet, this is filled with where the packet came from.
|
|
|
|
// recvfrom uses this to fill its address_out parameter.
|
2020-03-27 18:06:16 +00:00
|
|
|
};
|
|
|
|
|
2020-07-31 18:49:37 +01:00
|
|
|
/***************************************************
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-31 18:49:37 +01:00
|
|
|
UDP Bound Socket Tree Classes & Functions
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-31 18:49:37 +01:00
|
|
|
***************************************************/
|
2020-03-27 18:06:16 +00:00
|
|
|
|
|
|
|
class CUDPTreeQueue
|
|
|
|
{ // next, last for CQueue implementation.
|
2020-07-31 18:49:37 +01:00
|
|
|
CUDPTreeQueue *next;
|
|
|
|
CUDPTreeQueue *last;
|
|
|
|
CUDPSocket *socket;
|
2020-03-27 18:06:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class CUDPTreeNode
|
|
|
|
{
|
2020-07-31 18:49:37 +01:00
|
|
|
I64 port;
|
|
|
|
CUDPTreeNode *left;
|
|
|
|
CUDPTreeNode *right;
|
|
|
|
CUDPTreeQueue *queue;
|
2020-03-27 18:06:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
CUDPTreeNode *UDPTreeNodeInit()
|
|
|
|
{ // init new empty tree/node.
|
|
|
|
CUDPTreeNode *tree_node = CAlloc(sizeof(CUDPTreeNode));
|
|
|
|
|
|
|
|
return tree_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
U0 UDPTreeNodeAdd(CUDPTreeNode *node, CUDPTreeNode *tree)
|
|
|
|
{ // using temp and last allows avoiding recursion and non-growing stack issues.
|
|
|
|
CUDPTreeNode *temp_tree = tree;
|
|
|
|
CUDPTreeNode *last_tree = temp_tree;
|
|
|
|
|
|
|
|
while (temp_tree)
|
2020-07-28 06:26:09 +01:00
|
|
|
{ // loop ends when temp_tree hits a NULL node.
|
2020-03-27 18:06:16 +00:00
|
|
|
if (node->port < temp_tree->port)
|
|
|
|
{ // if node smaller, go left
|
|
|
|
last_tree = temp_tree;
|
|
|
|
temp_tree = temp_tree->left;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // if node equal or larger, go right
|
|
|
|
last_tree = temp_tree;
|
|
|
|
temp_tree = temp_tree->right;
|
2020-07-28 06:26:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// once while loop ends, this results in last_tree
|
|
|
|
// being the resulting tree to store the node inside of.
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 06:26:09 +01:00
|
|
|
// recompute the direction and set.
|
2020-03-27 18:06:16 +00:00
|
|
|
if (node->port < last_tree->port)// if node smaller, go left
|
|
|
|
last_tree->left = node;
|
|
|
|
else // if node equal or larger, go right
|
|
|
|
last_tree->right = node;
|
|
|
|
}
|
|
|
|
|
|
|
|
CUDPTreeNode *UDPTreeNodeParamAdd(I64 node_port, CUDPTreeNode *tree)
|
|
|
|
{ // add a node using params, return pointer to the node
|
|
|
|
CUDPTreeNode *result = UDPTreeNodeInit;
|
|
|
|
result->port = node_port;
|
|
|
|
|
|
|
|
UDPTreeNodeAdd(result, tree);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
CUDPTreeNode *UDPTreeNodeParamInit(I64 port)
|
|
|
|
{
|
|
|
|
CUDPTreeNode *result = UDPTreeNodeInit;
|
|
|
|
result->port = port;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
CUDPTreeNode *UDPTreeNodeFind(I64 port, CUDPTreeNode *tree)
|
|
|
|
{
|
|
|
|
CUDPTreeNode *temp_tree = tree;
|
|
|
|
|
|
|
|
while (temp_tree)
|
|
|
|
{
|
|
|
|
if (port < temp_tree->port) // if value smaller, go left
|
|
|
|
temp_tree = temp_tree->left;
|
|
|
|
else if (port > temp_tree->port) // if value larger, go right
|
|
|
|
temp_tree = temp_tree->right;
|
2020-07-28 06:26:09 +01:00
|
|
|
else // if value equal, match found.
|
2020-03-27 18:06:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return temp_tree; // ! NULL if not found.
|
|
|
|
}
|
|
|
|
|
|
|
|
CUDPTreeNode *UDPTreeNodePop(I64 port, CUDPTreeNode *tree)
|
|
|
|
{ // mimics TreeNodeFind. pops whole sub-tree, original tree loses whole branch.
|
|
|
|
CUDPTreeNode *parent_tree = tree;
|
2020-07-28 06:51:14 +01:00
|
|
|
CUDPTreeNode *temp_tree = parent_tree;
|
|
|
|
Bool is_left = FALSE;
|
2020-03-27 18:06:16 +00:00
|
|
|
Bool is_right = FALSE;
|
|
|
|
|
|
|
|
while (temp_tree)
|
|
|
|
{
|
|
|
|
if (port < temp_tree->port)
|
|
|
|
{
|
2020-07-28 06:51:14 +01:00
|
|
|
parent_tree = temp_tree;
|
|
|
|
temp_tree = temp_tree->left;
|
|
|
|
is_right = FALSE;
|
|
|
|
is_left = TRUE;
|
2020-03-27 18:06:16 +00:00
|
|
|
}
|
|
|
|
else if (port > temp_tree->port)
|
|
|
|
{
|
2020-07-28 06:51:14 +01:00
|
|
|
parent_tree = temp_tree;
|
|
|
|
temp_tree = temp_tree->right;
|
|
|
|
is_right = TRUE;
|
|
|
|
is_left = FALSE;
|
2020-03-27 18:06:16 +00:00
|
|
|
}
|
2020-07-28 06:26:09 +01:00
|
|
|
else // if value equal, match found.
|
2020-03-27 18:06:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (temp_tree)
|
|
|
|
{ //if we found it, clear its parents link to the node
|
|
|
|
if (is_left)
|
|
|
|
{
|
|
|
|
parent_tree->left = NULL;
|
|
|
|
}
|
|
|
|
else if (is_right)
|
|
|
|
{
|
|
|
|
parent_tree->right = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return temp_tree; // NULL if not found.
|
|
|
|
}
|
|
|
|
|
|
|
|
CUDPTreeNode *UDPTreeNodeSinglePop(I64 port, CUDPTreeNode *tree)
|
2020-07-28 06:26:09 +01:00
|
|
|
{ // pop a tree off, then add back in its sub-trees to main tree.
|
|
|
|
// original node sub-trees are cleared.
|
|
|
|
// TODO: double check this logic ensure it's sound.
|
2020-07-28 06:51:14 +01:00
|
|
|
CUDPTreeNode *node = UDPTreeNodePop(port, tree);
|
|
|
|
CUDPTreeNode *left = node->left;
|
|
|
|
CUDPTreeNode *right = node->right;
|
2020-03-27 18:06:16 +00:00
|
|
|
|
|
|
|
if (node)
|
|
|
|
{
|
|
|
|
if (left)
|
2020-07-28 06:26:09 +01:00
|
|
|
{ // if node has left tree, add the tree
|
2020-03-27 18:06:16 +00:00
|
|
|
UDPTreeNodeAdd(left, tree);
|
2020-07-28 06:26:09 +01:00
|
|
|
node->left = NULL;
|
2020-03-27 18:06:16 +00:00
|
|
|
}
|
|
|
|
if (right)
|
2020-07-28 06:26:09 +01:00
|
|
|
{ // if node has right tree, add the tree.
|
2020-03-27 18:06:16 +00:00
|
|
|
UDPTreeNodeAdd(right, tree);
|
2020-07-28 06:26:09 +01:00
|
|
|
node->right = NULL;
|
2020-03-27 18:06:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
2020-07-28 08:31:01 +01:00
|
|
|
/* TODO: determine if necessary to implement
|
2020-03-27 18:06:16 +00:00
|
|
|
U0 UDPTreeNodeFree(CUDPTreeNode *node)
|
2020-07-28 06:26:09 +01:00
|
|
|
{ // only clears and frees the node. !! if node has subtrees,
|
|
|
|
// they will be left floating. use with caution to avoid memory leaks
|
2020-03-27 18:06:16 +00:00
|
|
|
// ... uh.. what to do with the inner CTreeQueue floating around ..? we need to fix that too right?
|
2020-07-28 06:26:09 +01:00
|
|
|
// .. what does CQueue functions give us. QueueRemove is our best bet,
|
|
|
|
// i guess it will just try to swap around the next last ptrs.
|
2020-03-27 18:06:16 +00:00
|
|
|
}
|
2020-07-28 08:31:01 +01:00
|
|
|
*/
|
2020-03-27 18:06:16 +00:00
|
|
|
U0 UDPTreeNodeQueueInit(CUDPTreeNode *node)
|
|
|
|
{
|
|
|
|
node->queue = CAlloc(sizeof(CUDPTreeQueue));
|
|
|
|
QueueInit(node->queue);
|
|
|
|
}
|
|
|
|
|
2020-04-11 06:43:18 +01:00
|
|
|
U0 UDPTreeNodeQueueAdd(CUDPSocket *socket, CUDPTreeNode *node)
|
2020-03-27 18:06:16 +00:00
|
|
|
{
|
|
|
|
CUDPTreeQueue *new_entry;
|
|
|
|
|
|
|
|
if (!node->queue)
|
|
|
|
{
|
|
|
|
UDPTreeNodeQueueInit(node);
|
|
|
|
node->queue->socket = socket;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
new_entry = CAlloc(sizeof(CUDPTreeQueue));
|
|
|
|
QueueInit(new_entry);
|
|
|
|
new_entry->socket = socket;
|
|
|
|
QueueInsert(new_entry, node->queue->last);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-28 06:26:09 +01:00
|
|
|
// refactored to UDPTreeNodeQueueSocketFind for Socket-call level functions
|
2020-04-11 06:43:18 +01:00
|
|
|
CUDPTreeQueue *UDPTreeNodeQueueSocketFind(CUDPSocket *socket, CUDPTreeNode *node)
|
2020-03-27 18:06:16 +00:00
|
|
|
{
|
|
|
|
CUDPTreeQueue *temp_queue;
|
|
|
|
|
|
|
|
if (node->queue)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (node->queue->socket == socket)
|
|
|
|
return node->queue;
|
|
|
|
|
|
|
|
temp_queue = node->queue->next;
|
|
|
|
while (temp_queue != node->queue)
|
|
|
|
{
|
|
|
|
if (temp_queue->socket == socket)
|
|
|
|
return temp_queue;
|
|
|
|
temp_queue = temp_queue->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-07-28 06:51:14 +01:00
|
|
|
CUDPTreeQueue *UDPTreeNodeQueueIPV4Find(U32 address, CUDPTreeNode *node)
|
2020-08-07 07:39:07 +01:00
|
|
|
{ // address should be pulled from an instance of CIPV4Address (TODO... double check what bit order we're in ?)
|
|
|
|
// TODO: should INADDR_ANY entries be stored and looped, or keep current returning ASAP at INNADDR_ANY ?
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-08-07 07:39:07 +01:00
|
|
|
CUDPTreeQueue *temp_queue = node->queue;
|
|
|
|
CSocketAddressIPV4 *temp_ip;
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-08-07 07:39:07 +01:00
|
|
|
if (temp_queue)
|
2020-03-27 18:06:16 +00:00
|
|
|
{
|
2020-08-07 07:39:07 +01:00
|
|
|
do
|
2020-03-27 18:06:16 +00:00
|
|
|
{
|
2020-07-28 06:51:14 +01:00
|
|
|
if (temp_queue->socket->receive_address.family == AF_INET)
|
|
|
|
{
|
|
|
|
temp_ip = &temp_queue->socket->receive_address;
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithLog("UDPTreeNodeQueueIPV4Find: addr, nodequeue addr: %08X, %08X\n",
|
|
|
|
address, temp_ip->address.address);
|
|
|
|
|
|
|
|
if (temp_ip->address.address == address || temp_ip->address.address == INADDR_ANY)
|
|
|
|
{
|
|
|
|
ZenithLog("UDPTreeNodeQueueIPV4Find: Address match: addr, nodequeue: %08X, %08X \n",
|
|
|
|
address, temp_ip->address.address);
|
2020-07-28 06:51:14 +01:00
|
|
|
return temp_queue;
|
2020-08-07 07:39:07 +01:00
|
|
|
}
|
2020-07-28 06:51:14 +01:00
|
|
|
}
|
2020-03-27 18:06:16 +00:00
|
|
|
|
|
|
|
temp_queue = temp_queue->next;
|
|
|
|
}
|
2020-08-07 07:39:07 +01:00
|
|
|
while (temp_queue != node->queue);
|
2020-03-27 18:06:16 +00:00
|
|
|
}
|
|
|
|
|
2020-08-07 07:39:07 +01:00
|
|
|
return NULL;
|
2020-03-27 18:06:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CUDPTreeQueue *UDPTreeNodeQueueSocketSinglePop(CUDPSocket *socket, CUDPTreeNode *node)
|
|
|
|
{ // search by socket, pop a single UDPTreeQueue off the node, return popped queue.
|
|
|
|
CUDPTreeQueue *temp_queue = UDPTreeNodeQueueSocketFind(socket, node);
|
2020-07-28 06:26:09 +01:00
|
|
|
CUDPTreeQueue *temp_next;
|
|
|
|
CUDPTreeQueue *temp_last;
|
2020-03-27 18:06:16 +00:00
|
|
|
|
|
|
|
if (temp_queue)
|
2020-07-28 06:26:09 +01:00
|
|
|
{
|
|
|
|
temp_next = temp_queue->next;
|
|
|
|
temp_last = temp_queue->last;
|
|
|
|
|
|
|
|
if (temp_queue != temp_next)
|
|
|
|
{ // if 2 or more entries in queue, stitch next&last, loop found queue
|
2020-07-28 06:51:14 +01:00
|
|
|
temp_last->next = temp_next;
|
|
|
|
temp_next->last = temp_last;
|
2020-07-28 06:26:09 +01:00
|
|
|
temp_queue->next = temp_queue;
|
|
|
|
temp_queue->last = temp_queue;
|
2020-07-28 06:51:14 +01:00
|
|
|
|
2020-07-28 06:26:09 +01:00
|
|
|
if (temp_queue == node->queue) // if entry to pop is node queue head, change head to next
|
|
|
|
node->queue = temp_next;
|
|
|
|
}
|
|
|
|
else if (temp_queue == temp_next == temp_last)
|
|
|
|
node->queue = NULL; // only one entry in queue, NULL node-queue link and pop the queue.
|
|
|
|
}
|
2020-03-27 18:06:16 +00:00
|
|
|
return temp_queue; // if not found, NULL.
|
|
|
|
}
|
|
|
|
|
2020-07-28 06:26:09 +01:00
|
|
|
/*
|
2020-03-27 18:06:16 +00:00
|
|
|
CUDPTreeQueue *UDPTreeNodeQueueSinglePop(U32 address, CUDPTreeNode *node)
|
|
|
|
{ // pop a single UDPTreeQueue off the node, return popped queue.
|
|
|
|
CUDPTreeQueue *temp_queue = UDPTreeNodeQueueFind(address, node);
|
|
|
|
|
|
|
|
if (temp_queue)
|
2020-07-28 06:26:09 +01:00
|
|
|
Debug("When is this method needed ?");
|
|
|
|
|
|
|
|
//QueueRemove(temp_queue);
|
|
|
|
// links between queue entries pop out this and stitch back together. popped entry might have old links?
|
2020-03-27 18:06:16 +00:00
|
|
|
|
|
|
|
return temp_queue; // if not found, NULL.
|
|
|
|
}
|
2020-07-28 06:26:09 +01:00
|
|
|
*/
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-31 18:49:37 +01:00
|
|
|
/***************************************************/
|
2020-03-27 18:06:16 +00:00
|
|
|
|
|
|
|
class CUDPGlobals
|
|
|
|
{
|
|
|
|
|
2020-04-11 06:43:18 +01:00
|
|
|
CUDPTreeNode *bound_socket_tree;
|
2020-03-27 18:06:16 +00:00
|
|
|
|
|
|
|
} udp_globals;
|
|
|
|
|
|
|
|
|
|
|
|
U0 UDPGlobalsInit()
|
|
|
|
{
|
|
|
|
udp_globals.bound_socket_tree = NULL;
|
|
|
|
}
|
|
|
|
|
2020-04-11 06:43:18 +01:00
|
|
|
I64 UDPPacketAllocate(U8 **frame_out,
|
2020-07-28 06:51:14 +01:00
|
|
|
U32 source_ip,
|
|
|
|
U16 source_port,
|
|
|
|
U32 destination_ip,
|
|
|
|
U16 destination_port,
|
|
|
|
I64 length)
|
2020-03-27 18:06:16 +00:00
|
|
|
{
|
|
|
|
U8 *ethernet_frame;
|
|
|
|
I64 de_index;
|
2020-04-11 06:43:18 +01:00
|
|
|
CUDPHeader *header;
|
2020-03-27 18:06:16 +00:00
|
|
|
|
|
|
|
de_index = IPV4PacketAllocate(ðernet_frame,
|
2020-07-28 06:51:14 +01:00
|
|
|
IP_PROTOCOL_UDP,
|
|
|
|
source_ip,
|
|
|
|
destination_ip,
|
|
|
|
sizeof(CUDPHeader) + length);
|
2020-03-27 18:06:16 +00:00
|
|
|
if (de_index < 0)
|
|
|
|
{
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithLog("UDP PACKET ALLOCATE: Ethernet Frame Allocate failed.\n");
|
2020-03-27 18:06:16 +00:00
|
|
|
return de_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
header = ethernet_frame;
|
|
|
|
|
2020-07-28 06:51:14 +01:00
|
|
|
header->source_port = EndianU16(source_port);
|
|
|
|
header->destination_port = EndianU16(destination_port);
|
|
|
|
header->length = EndianU16(sizeof(CUDPHeader) + length);
|
|
|
|
header->checksum = 0;
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-08-07 07:39:07 +01:00
|
|
|
// ClassRep(header);
|
|
|
|
|
2020-03-27 18:06:16 +00:00
|
|
|
*frame_out = ethernet_frame + sizeof(CUDPHeader);
|
|
|
|
|
2020-08-07 07:39:07 +01:00
|
|
|
return de_index;
|
2020-03-27 18:06:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
U0 UDPPacketFinish(I64 de_index)
|
|
|
|
{ // alias for IPV4PacketFinish, alias for EthernetFrameFinish, alias for driver send packet
|
|
|
|
IPV4PacketFinish(de_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
I64 UDPParsePacket(U16 *source_port_out,
|
2020-07-28 06:51:14 +01:00
|
|
|
U16 *destination_port_out,
|
|
|
|
U8 **data_out,
|
|
|
|
I64 *length_out,
|
|
|
|
CIPV4Packet *packet)
|
2020-03-27 18:06:16 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
// check ip protocol? probably redundant
|
|
|
|
|
|
|
|
CUDPHeader *header = packet->data;
|
|
|
|
|
2020-07-28 08:31:01 +01:00
|
|
|
// TODO: Shrine has FIXME, validate packet length!
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 06:51:14 +01:00
|
|
|
*source_port_out = EndianU16(header->source_port);
|
|
|
|
*destination_port_out = EndianU16(header->destination_port);
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 06:51:14 +01:00
|
|
|
*data_out = packet->data + sizeof(CUDPHeader);
|
|
|
|
*length_out = packet->length - sizeof(CUDPHeader);
|
2020-03-27 18:06:16 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//CUDPSocket *UDPSocket(U16 domain, U16 type) // should this even be allowed? why not just UDPSocket; ? it could just know its domain and type.
|
2020-07-28 08:25:24 +01:00
|
|
|
CUDPSocket *UDPSocket(U16 domain=AF_UNSPEC)
|
2020-03-27 18:06:16 +00:00
|
|
|
{
|
|
|
|
U16 type = SOCKET_DATAGRAM;
|
|
|
|
CUDPSocket *udp_socket = CAlloc(sizeof(CUDPSocket));
|
|
|
|
|
|
|
|
udp_socket->socket = Socket(domain, type);
|
|
|
|
|
2020-07-28 08:25:24 +01:00
|
|
|
udp_socket->receive_address.family = domain; // INET, INET6, or unspecified
|
2020-03-27 18:06:16 +00:00
|
|
|
|
|
|
|
return udp_socket;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-07-28 08:25:24 +01:00
|
|
|
I64 UDPSocketBind(CUDPSocket *udp_socket, CSocketAddressStorage *address_source)
|
2020-03-27 18:06:16 +00:00
|
|
|
{
|
|
|
|
CUDPTreeNode *temp_node;
|
2020-07-28 08:25:24 +01:00
|
|
|
CSocketAddressIPV4 *ipv4_source;
|
|
|
|
CSocketAddressIPV4 *ipv4_receive;
|
|
|
|
CSocketAddressIPV6 *ipv6_source;
|
|
|
|
CSocketAddressIPV6 *ipv6_receive;
|
2020-03-27 18:06:16 +00:00
|
|
|
U16 port;
|
|
|
|
|
|
|
|
switch (udp_socket->socket->state)
|
|
|
|
{
|
|
|
|
case SOCKET_STATE_READY: // Socket State machine must be in init state
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithErr("UDP SOCKET BIND: Failed, Socket state-machine must be in READY state.\n");
|
2020-03-27 18:06:16 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udp_socket->bound_to)
|
|
|
|
{
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithErr("UDP SOCKET BIND: UDP Socket currently Bound.\n");
|
2020-03-27 18:06:16 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-07-28 08:25:24 +01:00
|
|
|
switch (address_source->family)
|
|
|
|
{
|
|
|
|
case AF_INET:
|
|
|
|
|
|
|
|
if (udp_socket->receive_address.family == AF_INET6)
|
|
|
|
{
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithErr("UDP SOCKET BIND: Incompatible Address type.\n");
|
2020-07-28 08:25:24 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-07-28 08:31:01 +01:00
|
|
|
ipv4_source = address_source;
|
2020-07-28 08:25:24 +01:00
|
|
|
ipv4_receive = &udp_socket->receive_address;
|
|
|
|
|
2020-07-28 08:31:01 +01:00
|
|
|
ipv4_receive->address.address = ipv4_source->address.address; // bind socket to address in parameter.
|
|
|
|
ipv4_receive->port = ipv4_source->port; // ... consistency would say keep in Big Endian ...
|
2020-07-28 08:25:24 +01:00
|
|
|
|
|
|
|
port = EndianU16(ipv4_source->port); // port member should be Big Endian, so now we're going L.E (?)
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AF_INET6:
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 08:25:24 +01:00
|
|
|
if (udp_socket->receive_address.family == AF_INET)
|
|
|
|
{
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithErr("UDP SOCKET BIND: Incompatible Address type.\n");
|
2020-07-28 08:25:24 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ipv6_source = address_source;
|
2020-07-28 08:31:01 +01:00
|
|
|
ipv6_receive = &udp_socket->receive_address;
|
|
|
|
// ...
|
2020-07-28 08:25:24 +01:00
|
|
|
// ...
|
2020-07-28 06:34:39 +01:00
|
|
|
|
2020-07-28 08:25:24 +01:00
|
|
|
port = EndianU16(ipv6_source->port); // port member should be Big Endian, so now we're going L.E (?)
|
|
|
|
|
|
|
|
Debug("TODO: IPV6 UDP BIND");
|
|
|
|
|
|
|
|
break;
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 08:25:24 +01:00
|
|
|
case AF_UNSPEC:
|
2020-08-07 07:39:07 +01:00
|
|
|
Debug("TODO: AF_UNSPEC UDP BIND -- param family");
|
2020-07-28 08:25:24 +01:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// at this point, Socket and Address have matching family values
|
2020-03-27 18:06:16 +00:00
|
|
|
|
|
|
|
if (udp_globals.bound_socket_tree)
|
|
|
|
{
|
|
|
|
// look for our port.
|
|
|
|
temp_node = UDPTreeNodeFind(port, udp_globals.bound_socket_tree);
|
|
|
|
|
|
|
|
if (temp_node)
|
|
|
|
{ // if we find we have bound sockets at port, check address before adding to queue
|
2020-07-28 08:25:24 +01:00
|
|
|
switch (address_source->family)
|
2020-03-27 18:06:16 +00:00
|
|
|
{
|
2020-07-28 08:25:24 +01:00
|
|
|
case AF_INET:
|
2020-08-07 07:39:07 +01:00
|
|
|
// TODO: will any INADDR_ANY sockets bound at the port break this?
|
2020-07-28 08:25:24 +01:00
|
|
|
if (UDPTreeNodeQueueIPV4Find(ipv4_receive->address.address, temp_node))
|
|
|
|
{
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithErr("UDP SOCKET BIND: Address already in Bound Socket Tree !\n");
|
2020-07-28 08:25:24 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // if no address match, free to add socket to the node queue
|
|
|
|
UDPTreeNodeQueueAdd(udp_socket, temp_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
case AF_INET6:
|
|
|
|
Debug("TODO: IPV6 UDP BIND");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AF_UNSPEC:
|
2020-08-07 07:39:07 +01:00
|
|
|
Debug("TODO: AF_UNSPEC UDP BIND -- found in bound tree");
|
2020-07-28 08:25:24 +01:00
|
|
|
break;
|
2020-03-27 18:06:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // if we get no node back from port search, we didn't find it and are free to add a new node.
|
|
|
|
temp_node = UDPTreeNodeParamAdd(port, udp_globals.bound_socket_tree); // add new node with port, return its *.
|
|
|
|
UDPTreeNodeQueueAdd(udp_socket, temp_node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else // if no bound sockets, we init the tree as a new node
|
|
|
|
{
|
|
|
|
udp_globals.bound_socket_tree = UDPTreeNodeParamInit(port); //... shouuuld be in L.E .. ?
|
|
|
|
UDPTreeNodeQueueAdd(udp_socket, udp_globals.bound_socket_tree); // add the udp socket to the port queue
|
|
|
|
// maybe more checks to do before this, dunno rn.
|
|
|
|
}
|
|
|
|
|
|
|
|
udp_socket->bound_to = port;
|
|
|
|
|
|
|
|
SocketBind(udp_socket->socket); // Advance Socket state-machine to BIND REQ state.
|
|
|
|
|
|
|
|
switch (udp_socket->socket->state)
|
|
|
|
{
|
|
|
|
case SOCKET_STATE_BIND_REQ: // if BIND request success, set BOUND.
|
|
|
|
udp_socket->socket->state = SOCKET_STATE_BOUND;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithErr("UDP SOCKET BIND: Failed, Misconfigured Socket state-machine.\n");
|
2020-03-27 18:06:16 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
I64 UDPSocketClose(CUDPSocket *udp_socket)
|
|
|
|
{ // close, pop, and free the socket from the bound tree.
|
|
|
|
CUDPTreeNode *node;
|
|
|
|
CUDPTreeQueue *queue;
|
|
|
|
|
2020-07-28 08:31:01 +01:00
|
|
|
SocketClose(udp_socket->socket); // TODO: testing on closing a socket while another task is using it
|
|
|
|
// after low-level socket close, even if protocol level socket fails close, it is now disabled (state is close request)
|
|
|
|
|
2020-03-27 18:06:16 +00:00
|
|
|
node = UDPTreeNodeFind(udp_socket->bound_to, udp_globals.bound_socket_tree);
|
|
|
|
|
|
|
|
if (node)
|
|
|
|
queue = UDPTreeNodeQueueSocketFind(udp_socket, node);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Debug("Didn't find node at socket during UDPSocketClose!\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (queue)
|
|
|
|
{
|
2020-07-28 06:30:18 +01:00
|
|
|
UDPTreeNodeQueueSocketSinglePop(udp_socket, node);
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 06:30:18 +01:00
|
|
|
Free(udp_socket->socket);
|
2020-07-28 06:34:39 +01:00
|
|
|
// Free(udp_socket->receive_buffer); // i think we'll still need to keep this
|
2020-07-28 06:30:18 +01:00
|
|
|
Free(udp_socket);
|
|
|
|
Free(queue);
|
2020-03-27 18:06:16 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Debug("Didn't find queue at socket during UDPSocketClose!\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// UDPSocketConnect (Shrine just has FIXME: 'implement')
|
|
|
|
|
2020-07-28 06:34:39 +01:00
|
|
|
// UDPListen (Shrine just has no_warns, not implemented)
|
|
|
|
|
2020-07-28 08:31:01 +01:00
|
|
|
I64 UDPSocketReceiveFrom(CUDPSocket *udp_socket, U8 *buffer, I64 len, CSocketAddressStorage *address_out)
|
2020-07-28 06:34:39 +01:00
|
|
|
{ // ommitted I64 addrlen, flags not implemented
|
|
|
|
CSocketAddressIPV4 *ipv4_socket_addr;
|
|
|
|
CSocketAddressIPV6 *ipv6_socket_addr;
|
|
|
|
|
2020-07-28 08:31:01 +01:00
|
|
|
switch (udp_socket->socket->state)
|
|
|
|
{
|
|
|
|
case SOCKET_STATE_OPEN: // Socket State machine must
|
|
|
|
case SOCKET_STATE_BOUND: // be in connected or bound state
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithErr("UDP SOCKET RECEIVE FROM: Socket state-machine must be in OPEN or BOUND state.\n");
|
2020-07-28 08:31:01 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-07-28 06:34:39 +01:00
|
|
|
udp_socket->receive_buffer = buffer;
|
|
|
|
udp_socket->receive_len = len;
|
|
|
|
|
|
|
|
if (udp_socket->receive_timeout_ms != 0)
|
|
|
|
udp_socket->receive_max_timeout = counts.jiffies + udp_socket->receive_timeout_ms * JIFFY_FREQ / 1000;
|
|
|
|
|
2020-08-07 07:39:07 +01:00
|
|
|
// ClassRep(udp_socket);
|
|
|
|
ZenithLog("UDP SOCKET RECEIVE FROM: udp_socket->receive_buffer: 0x%0X.\n", udp_socket->receive_buffer);
|
2020-07-28 06:34:39 +01:00
|
|
|
while (udp_socket->receive_buffer != NULL)
|
|
|
|
{ // 'Check for timeout'
|
|
|
|
if (udp_socket->receive_timeout_ms != 0 && counts.jiffies > udp_socket->receive_max_timeout)
|
|
|
|
{ // Shrine has TODO: 'seterror(EWOULDBLOCK)' investigate this
|
|
|
|
udp_socket->receive_len = -1; // ?
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithErr("UDP SOCKET RECEIVE FROM: Timed out.\n");
|
2020-07-28 06:34:39 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Yield;
|
|
|
|
}
|
|
|
|
|
2020-07-28 08:31:01 +01:00
|
|
|
if (address_out)
|
2020-07-28 06:34:39 +01:00
|
|
|
{
|
2020-08-07 07:39:07 +01:00
|
|
|
// switch (udp_socket->receive_address.family)
|
|
|
|
switch (udp_socket->from_address.family)
|
2020-07-28 06:34:39 +01:00
|
|
|
{
|
|
|
|
case AF_INET:
|
2020-07-28 08:31:01 +01:00
|
|
|
ipv4_socket_addr = address_out;
|
2020-08-07 07:39:07 +01:00
|
|
|
// MemCopy(ipv4_socket_addr, &udp_socket->receive_address, sizeof(CSocketAddressIPV4));
|
|
|
|
MemCopy(ipv4_socket_addr, &udp_socket->from_address, sizeof(CSocketAddressIPV4));
|
2020-07-28 06:34:39 +01:00
|
|
|
break;
|
|
|
|
case AF_INET6:
|
2020-07-28 08:31:01 +01:00
|
|
|
ipv6_socket_addr = address_out;
|
2020-08-07 07:39:07 +01:00
|
|
|
// MemCopy(ipv6_socket_addr, &udp_socket->receive_address, sizeof(CSocketAddressIPV6));
|
|
|
|
MemCopy(ipv6_socket_addr, &udp_socket->from_address, sizeof(CSocketAddressIPV6));
|
2020-07-28 06:34:39 +01:00
|
|
|
break;
|
2020-07-28 08:31:01 +01:00
|
|
|
case AF_UNSPEC:
|
|
|
|
Debug("TODO: UDP Receive From Error AF_UNSPEC UDPSocket Address Family\n");
|
|
|
|
break;
|
2020-07-28 06:34:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-28 08:31:01 +01:00
|
|
|
SocketReceiveFrom(udp_socket->socket);
|
|
|
|
|
2020-07-28 06:34:39 +01:00
|
|
|
return udp_socket->receive_len;
|
|
|
|
}
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 08:31:01 +01:00
|
|
|
I64 UDPSocketSendTo(CUDPSocket *udp_socket, U8 *buffer, I64 len, CSocketAddressStorage *destination_addr)
|
|
|
|
{
|
|
|
|
CSocketAddressStorage *dest;
|
|
|
|
CSocketAddressIPV4 *ipv4_destination;
|
|
|
|
CSocketAddressIPV6 *ipv6_destination;
|
|
|
|
U8 *ethernet_frame;
|
|
|
|
I64 de_index;
|
|
|
|
|
|
|
|
switch (udp_socket->socket->state)
|
|
|
|
{
|
|
|
|
case SOCKET_STATE_OPEN: // Socket State machine must
|
|
|
|
case SOCKET_STATE_BOUND: // be in connected or bound state for send.
|
|
|
|
dest = &udp_socket->receive_address; // if already bound, ignore param destination
|
|
|
|
break; // and use stored address as send address.
|
|
|
|
|
|
|
|
case SOCKET_STATE_READY: // If socket state is initial, attempt to bind it to destination.
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithLog("UDP SOCKET SEND TO: Socket unbound. Attempting Bind at address parameter.\n");
|
2020-07-28 08:31:01 +01:00
|
|
|
UDPSocketBind(udp_socket, destination_addr);
|
|
|
|
dest = destination_addr;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithErr("UDP SOCKET SEND TO: Socket state-machine must be in OPEN, BOUND or READY state.\n");
|
2020-07-28 08:31:01 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (dest->family)
|
|
|
|
{
|
|
|
|
case AF_INET:
|
|
|
|
ipv4_destination = dest;
|
|
|
|
|
|
|
|
de_index = UDPPacketAllocate(ðernet_frame,
|
|
|
|
IPV4GetAddress(),
|
|
|
|
0,
|
|
|
|
EndianU32(ipv4_destination->address.address),
|
|
|
|
EndianU16(ipv4_destination->port),
|
|
|
|
len); // is get address parens redundant?
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
ipv6_destination = dest;
|
|
|
|
Debug("TODO: IPV6 Not implemented yet");
|
|
|
|
break;
|
|
|
|
case AF_UNSPEC:
|
|
|
|
Debug("TODO: Error UDP Send To AF_UNSPEC\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (de_index < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
MemCopy(ethernet_frame, buffer, len); // copies the data in buffer param into the ethernet frame
|
|
|
|
|
|
|
|
UDPPacketFinish(de_index);
|
|
|
|
return 0;
|
|
|
|
}
|
2020-03-27 18:06:16 +00:00
|
|
|
|
|
|
|
// UDPSocketSetOpt ?
|
|
|
|
|
2020-07-28 08:36:21 +01:00
|
|
|
I64 UDPHandler(CIPV4Packet *packet)
|
|
|
|
{ // TODO: Need either two UDP handlers for IPv4/IPv6, or logic changes if IPV6 is desired.
|
2020-08-07 07:39:07 +01:00
|
|
|
U16 source_port;
|
|
|
|
U16 destination_port;
|
|
|
|
U8 *data;
|
|
|
|
I64 length;
|
|
|
|
CUDPTreeNode *node;
|
|
|
|
CUDPTreeQueue *queue;
|
|
|
|
CUDPSocket *udp_socket;
|
|
|
|
CSocketAddressIPV4 *ipv4_addr;
|
|
|
|
I64 num_receive;
|
|
|
|
|
|
|
|
ZenithLog("UDP HANDLER: Beginning handling UDP Packet.\n");
|
|
|
|
/* ZenithWarn("UDP HANDLER: Yielding for a little bit as a debug.\n");
|
|
|
|
ZenithWarn("UDP HANDLER: ...\n");
|
|
|
|
I64 c = counts.jiffies;
|
|
|
|
while (counts.jiffies < c + 1000)
|
|
|
|
Yield;
|
|
|
|
ZenithWarn("UDP HANDLER: ...\n");*/
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 08:36:21 +01:00
|
|
|
I64 error = UDPParsePacket(&source_port, &destination_port, &data, &length, packet);
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 08:36:21 +01:00
|
|
|
if (error < 0)
|
|
|
|
{
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithErr("UDP HANDLER: Packet Parse Error.\n");
|
2020-07-28 08:36:21 +01:00
|
|
|
return error;
|
|
|
|
}
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 08:36:21 +01:00
|
|
|
if (udp_globals.bound_socket_tree)
|
|
|
|
{
|
|
|
|
node = UDPTreeNodeFind(destination_port, udp_globals.bound_socket_tree);
|
|
|
|
if (node)
|
|
|
|
{
|
|
|
|
queue = UDPTreeNodeQueueIPV4Find(packet->destination_ip_address, node); // TODO: make sure bit order is correct here!!
|
|
|
|
if (queue)
|
|
|
|
{
|
|
|
|
udp_socket = queue->socket;
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithLog("UDP HANDLER: Port and Address are in bound tree.\n");
|
2020-07-28 08:36:21 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithWarn("UDP HANDLER: Found node for port, but address is not in node queue.\n");
|
|
|
|
ZenithWarn(" UDP packet dest ip: 0x%0X.\n", packet->destination_ip_address);
|
2020-07-28 08:36:21 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithWarn("UDP HANDLER: Node for Port is not in tree.\n");
|
2020-07-28 08:36:21 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithWarn("UDP HANDLER: Socket tree is currently empty.\n");
|
2020-07-28 08:36:21 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithLog("UDP HANDLER: Checking if UDP Socket's Receive-Buffer exists. UDPSocket at: 0x%0X \n", udp_socket);
|
|
|
|
ZenithLog(" It probably exists, wtf going on ? udp_socket->receive_buffer: 0x%0X.\n", udp_socket->receive_buffer);
|
2020-07-28 08:36:21 +01:00
|
|
|
// at this point, udp_socket is set, otherwise has already returned -1.
|
|
|
|
if (udp_socket->receive_buffer)
|
|
|
|
{
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithLog("UDP HANDLER: Saw UDP Socket receive buffer exists, about to copy data into it.\n");
|
2020-07-28 08:36:21 +01:00
|
|
|
num_receive = udp_socket->receive_len;
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 08:36:21 +01:00
|
|
|
if (num_receive > length)
|
|
|
|
{
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithWarn("UDP HANDLER: Truncating UDP socket receive length. num_receive , len : %d, %d\n",
|
|
|
|
num_receive, length);
|
2020-07-28 08:36:21 +01:00
|
|
|
num_receive = length;
|
|
|
|
}
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 08:36:21 +01:00
|
|
|
MemCopy(udp_socket->receive_buffer, data, num_receive);
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 08:36:21 +01:00
|
|
|
// Shrine has comment 'signal that we received something'
|
|
|
|
// In UDPSocketRecvFrom, a given buffer is set as receive buffer.
|
|
|
|
// Handler sees socket has buffer, copies data to that buffer,
|
|
|
|
// then clears the socket's pointer for it. Actual buffer location
|
|
|
|
// itself is untouched.
|
|
|
|
udp_socket->receive_buffer = NULL;
|
|
|
|
udp_socket->receive_len = num_receive;
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-08-07 07:39:07 +01:00
|
|
|
// ipv4_addr = &udp_socket->receive_address;
|
|
|
|
ipv4_addr = &udp_socket->from_address;
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-08-07 07:39:07 +01:00
|
|
|
ipv4_addr->family = AF_INET;
|
2020-07-28 08:36:21 +01:00
|
|
|
ipv4_addr->port = EndianU16(source_port);
|
|
|
|
ipv4_addr->address.address = EndianU32(packet->source_ip_address);
|
2020-08-07 07:39:07 +01:00
|
|
|
ZenithLog("UDP HANDLER: Copying packet source IP (BE) to FROM_ADDRESS of UDP Socket: %08X \n", ipv4_addr->address.address);
|
2020-07-28 08:36:21 +01:00
|
|
|
}
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 08:36:21 +01:00
|
|
|
return error;
|
|
|
|
}
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-28 08:36:21 +01:00
|
|
|
// the socket functions just act on the socket state machine.
|
|
|
|
// ZenithErr and return fail vals if socket FSM improperly used.
|
|
|
|
// Careful with Free()'s.
|
2020-03-27 18:06:16 +00:00
|
|
|
|
2020-07-31 18:49:37 +01:00
|
|
|
UDPGlobalsInit;
|