+ Log_Debug("UDP", "hdr->SourcePort = %i", ntohs(hdr->SourcePort));
+ Log_Debug("UDP", "hdr->DestPort = %i", ntohs(hdr->DestPort));
+ Log_Debug("UDP", "hdr->Length = %i", ntohs(hdr->Length));
+ Log_Debug("UDP", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum));
+
+ // Check registered connections
+ Mutex_Acquire(&glUDP_Channels);
+ UDP_int_ScanList(gpUDP_Channels, Interface, Address, Length, Buffer);
+ Mutex_Release(&glUDP_Channels);
+}
+
+/**
+ * \brief Handle an ICMP Unrechable Error
+ */
+void UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer)
+{
+
+}
+
+/**
+ * \brief Send a packet
+ * \param Channel Channel to send the packet from
+ * \param Data Packet data
+ * \param Length Length in bytes of packet data
+ */
+void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, void *Address, Uint16 Port, void *Data, size_t Length)
+{
+ tUDPHeader *hdr;
+
+ if(Channel->Interface && Channel->Interface->Type != AddrType) return ;
+
+ switch(AddrType)
+ {
+ case 4:
+ // Create the packet
+ hdr = malloc(sizeof(tUDPHeader)+Length);
+ hdr->SourcePort = htons( Channel->LocalPort );
+ hdr->DestPort = htons( Port );
+ hdr->Length = htons( sizeof(tUDPHeader) + Length );
+ hdr->Checksum = 0; // Checksum can be zero on IPv4
+ memcpy(hdr->Data, Data, Length);
+ // Pass on the the IPv4 Layer
+ // TODO: What if Channel->Interface is NULL here?
+ IPv4_SendPacket(Channel->Interface, *(tIPv4*)Address, IP4PROT_UDP, 0, sizeof(tUDPHeader)+Length, hdr);
+ // Free allocated packet
+ free(hdr);
+ break;
+ }
+}
+
+// --- Client Channels
+tVFS_Node *UDP_Channel_Init(tInterface *Interface)
+{
+ tUDPChannel *new;
+ new = calloc( sizeof(tUDPChannel), 1 );
+ new->Interface = Interface;
+ new->Node.ImplPtr = new;
+ new->Node.NumACLs = 1;
+ new->Node.ACLs = &gVFS_ACL_EveryoneRW;
+ new->Node.Read = UDP_Channel_Read;
+ new->Node.Write = UDP_Channel_Write;
+ new->Node.IOCtl = UDP_Channel_IOCtl;
+ new->Node.Close = UDP_Channel_Close;
+
+ Mutex_Acquire(&glUDP_Channels);
+ new->Next = gpUDP_Channels;
+ gpUDP_Channels = new;
+ Mutex_Release(&glUDP_Channels);
+
+ return &new->Node;
+}
+
+/**
+ * \brief Read from the channel file (wait for a packet)
+ */
+Uint64 UDP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tUDPChannel *chan = Node->ImplPtr;
+ tUDPPacket *pack;
+ tUDPEndpoint *ep;
+ int ofs;
+
+ if(chan->LocalPort == 0) {
+ Log_Notice("UDP", "Channel %p sent with no local port", chan);
+ return 0;
+ }
+
+ while(chan->Queue == NULL) Threads_Yield();
+
+ for(;;)
+ {
+ VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "UDP_Channel_Read");
+ SHORTLOCK(&chan->lQueue);
+ if(chan->Queue == NULL) {
+ SHORTREL(&chan->lQueue);
+ continue;
+ }
+ pack = chan->Queue;
+ chan->Queue = pack->Next;
+ if(!chan->Queue) {
+ chan->QueueEnd = NULL;
+ VFS_MarkAvaliable(Node, 0); // Nothing left
+ }
+ SHORTREL(&chan->lQueue);
+ break;
+ }
+
+ // Check that the header fits
+ ep = Buffer;
+ ofs = 4 + IPStack_GetAddressSize(pack->Remote.AddrType);
+ if(Length < ofs) {
+ free(pack);
+ Log_Notice("UDP", "Insuficient space for header in buffer (%i < %i)", (int)Length, ofs);
+ return 0;
+ }
+
+ // Fill header
+ ep->Port = pack->Remote.Port;
+ ep->AddrType = pack->Remote.AddrType;
+ memcpy(&ep->Addr, &pack->Remote.Addr, IPStack_GetAddressSize(pack->Remote.AddrType));
+
+ // Copy packet data
+ if(Length > ofs + pack->Length) Length = ofs + pack->Length;
+ memcpy((char*)Buffer + ofs, pack->Data, Length - ofs);
+
+ // Free cached packet
+ free(pack);
+
+ return Length;
+}
+
+/**
+ * \brief Write to the channel file (send a packet)
+ */
+Uint64 UDP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tUDPChannel *chan = Node->ImplPtr;
+ tUDPEndpoint *ep;
+ void *data;
+ int ofs;
+ if(chan->LocalPort == 0) return 0;
+
+ ep = Buffer;
+ ofs = 2 + 2 + IPStack_GetAddressSize( ep->AddrType );
+
+ data = (char*)Buffer + ofs;
+
+ UDP_SendPacketTo(chan, ep->AddrType, &ep->Addr, ep->Port, Buffer, (size_t)Length - ofs);
+
+ return 0;
+}
+
+/**
+ * \brief Names for channel IOCtl Calls
+ */
+static const char *casIOCtls_Channel[] = {
+ DRV_IOCTLNAMES,
+ "getset_localport",
+ "getset_remoteport",
+ "getset_remotemask",
+ "set_remoteaddr",
+ NULL
+ };
+/**
+ * \brief Channel IOCtls
+ */
+int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ tUDPChannel *chan = Node->ImplPtr;
+ ENTER("pNode iID pData", Node, ID, Data);
+ switch(ID)
+ {
+ BASE_IOCTLS(DRV_TYPE_MISC, "UDP Channel", 0x100, casIOCtls_Channel);
+
+ case 4: // getset_localport (returns bool success)
+ if(!Data) LEAVE_RET('i', chan->LocalPort);
+ if(!CheckMem( Data, sizeof(Uint16) ) ) {
+ LOG("Invalid pointer %p", Data);
+ LEAVE_RET('i', -1);
+ }
+ // Set port
+ chan->LocalPort = *(Uint16*)Data;
+ // Permissions check (Ports lower than 1024 are root-only)
+ if(chan->LocalPort != 0 && chan->LocalPort < 1024) {
+ if( Threads_GetUID() != 0 ) {
+ LOG("Attempt by non-superuser to listen on port %i", chan->LocalPort);
+ chan->LocalPort = 0;
+ LEAVE_RET('i', -1);
+ }
+ }
+ // Allocate a random port if requested
+ if( chan->LocalPort == 0 )
+ chan->LocalPort = UDP_int_AllocatePort();
+ else
+ {
+ // Else, mark the requested port as used
+ if( UDP_int_MarkPortAsUsed(chan->LocalPort) == 0 ) {
+ LOG("Port %i us currently in use", chan->LocalPort);
+ chan->LocalPort = 0;
+ LEAVE_RET('i', 0);
+ }
+ LEAVE_RET('i', 1);
+ }
+ LEAVE_RET('i', 1);
+
+ case 5: // getset_remoteport (returns bool success)
+ if(!Data) LEAVE_RET('i', chan->Remote.Port);
+ if(!CheckMem( Data, sizeof(Uint16) ) ) {
+ LOG("Invalid pointer %p", Data);
+ LEAVE_RET('i', -1);
+ }
+ chan->Remote.Port = *(Uint16*)Data;
+ return 1;
+
+ case 6: // getset_remotemask (returns bool success)
+ if(!Data) LEAVE_RET('i', chan->RemoteMask);
+ if(!CheckMem(Data, sizeof(int))) LEAVE_RET('i', -1);
+ if( !chan->Interface ) {
+ LOG("Can't set remote mask on NULL interface");
+ LEAVE_RET('i', -1);
+ }
+ if( *(int*)Data > IPStack_GetAddressSize(chan->Interface->Type) )
+ LEAVE_RET('i', -1);
+ chan->RemoteMask = *(int*)Data;
+ return 1;
+
+ case 7: // set_remoteaddr (returns bool success)
+ if( !chan->Interface ) {
+ LOG("Can't set remote address on NULL interface");
+ LEAVE_RET('i', -1);
+ }
+ if(!CheckMem(Data, IPStack_GetAddressSize(chan->Interface->Type))) {
+ LOG("Invalid pointer");
+ LEAVE_RET('i', -1);
+ }
+ memcpy(&chan->Remote.Addr, Data, IPStack_GetAddressSize(chan->Interface->Type));
+ return 0;
+ }
+ LEAVE_RET('i', 0);
+}
+
+/**
+ * \brief Close and destroy an open channel
+ */
+void UDP_Channel_Close(tVFS_Node *Node)
+{
+ tUDPChannel *chan = Node->ImplPtr;
+ tUDPChannel *prev;
+
+ // Remove from the main list first
+ Mutex_Acquire(&glUDP_Channels);
+ if(gpUDP_Channels == chan)
+ gpUDP_Channels = gpUDP_Channels->Next;
+ else
+ {
+ for(prev = gpUDP_Channels;
+ prev->Next && prev->Next != chan;
+ prev = prev->Next);
+ if(!prev->Next)
+ Log_Warning("UDP", "Bookeeping Fail, channel %p is not in main list", chan);
+ else
+ prev->Next = prev->Next->Next;
+ }
+ Mutex_Release(&glUDP_Channels);
+
+ // Clear Queue
+ SHORTLOCK(&chan->lQueue);
+ while(chan->Queue)
+ {
+ tUDPPacket *tmp;
+ tmp = chan->Queue;
+ chan->Queue = tmp->Next;
+ free(tmp);
+ }
+ SHORTREL(&chan->lQueue);
+
+ // Free channel structure
+ free(chan);
+}
+
+/**
+ * \return Port Number on success, or zero on failure
+ */
+Uint16 UDP_int_AllocatePort()
+{
+ int i;
+ Mutex_Acquire(&glUDP_Ports);
+ // Fast Search
+ for( i = UDP_ALLOC_BASE; i < 0x10000; i += 32 )
+ if( gUDP_Ports[i/32] != 0xFFFFFFFF )
+ break;
+ if(i == 0x10000) return 0;
+ for( ;; i++ )
+ {
+ if( !(gUDP_Ports[i/32] & (1 << (i%32))) )
+ return i;
+ }
+ Mutex_Release(&glUDP_Ports);
+}
+
+/**
+ * \brief Allocate a specific port
+ * \return Boolean Success
+ */
+int UDP_int_MarkPortAsUsed(Uint16 Port)
+{
+ Mutex_Acquire(&glUDP_Ports);
+ if( gUDP_Ports[Port/32] & (1 << (Port%32)) ) {
+ return 0;
+ Mutex_Release(&glUDP_Ports);
+ }
+ gUDP_Ports[Port/32] |= 1 << (Port%32);
+ Mutex_Release(&glUDP_Ports);
+ return 1;
+}
+
+/**
+ * \brief Free an allocated port
+ */
+void UDP_int_FreePort(Uint16 Port)
+{
+ Mutex_Acquire(&glUDP_Ports);
+ gUDP_Ports[Port/32] &= ~(1 << (Port%32));
+ Mutex_Release(&glUDP_Ports);