+// --- Server
+tVFS_Node *TCP_Server_Init(tInterface *Interface)
+{
+ tTCPListener *srv;
+
+ srv = malloc( sizeof(tTCPListener) );
+
+ Log_Debug("TCP", "srv = %p", srv);
+
+ if( srv == NULL ) {
+ Log_Warning("TCP", "malloc failed for listener (%i) bytes", sizeof(tTCPListener));
+ return NULL;
+ }
+
+ srv->Interface = Interface;
+ srv->Port = 0;
+ srv->NextID = 0;
+ srv->Connections = NULL;
+ srv->Next = NULL;
+ srv->Node.Flags = VFS_FFLAG_DIRECTORY;
+ srv->Node.Size = -1;
+ srv->Node.ImplPtr = srv;
+ srv->Node.NumACLs = 1;
+ srv->Node.ACLs = &gVFS_ACL_EveryoneRW;
+ srv->Node.ReadDir = TCP_Server_ReadDir;
+ srv->Node.FindDir = TCP_Server_FindDir;
+ srv->Node.IOCtl = TCP_Server_IOCtl;
+ srv->Node.Close = TCP_Server_Close;
+
+ LOCK(&glTCP_Listeners);
+ srv->Next = gTCP_Listeners;
+ gTCP_Listeners = srv;
+ RELEASE(&glTCP_Listeners);
+
+ return &srv->Node;
+}
+
+/**
+ * \brief Wait for a new connection and return the connection ID
+ * \note Blocks until a new connection is made
+ * \param Node Server node
+ * \param Pos Position (ignored)
+ */
+char *TCP_Server_ReadDir(tVFS_Node *Node, int Pos)
+{
+ tTCPListener *srv = Node->ImplPtr;
+ tTCPConnection *conn;
+ char *ret;
+
+ Log_Log("TCP", "Thread %i waiting for a connection", Threads_GetTID());
+ for(;;)
+ {
+ LOCK( &srv->lConnections );
+ if( srv->NewConnections != NULL ) break;
+ RELEASE( &srv->lConnections );
+ Threads_Yield();
+ continue;
+ }
+
+
+ // Increment the new list (the current connection is still on the
+ // normal list
+ conn = srv->NewConnections;
+ srv->NewConnections = conn->Next;
+
+ RELEASE( &srv->lConnections );
+
+ ret = malloc(9);
+ itoa(ret, Node->ImplInt, 16, 8, '0');
+ Log("TCP_Server_ReadDir: RETURN '%s'", ret);
+ return ret;
+}
+
+/**
+ * \brief Gets a client connection node
+ * \param Node Server node
+ * \param Name Hexadecimal ID of the node
+ */
+tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, char *Name)
+{
+ tTCPConnection *conn;
+ tTCPListener *srv = Node->ImplPtr;
+ char tmp[9];
+ int id = atoi(Name);
+
+ // Sanity Check
+ itoa(tmp, id, 16, 8, '0');
+ if(strcmp(tmp, Name) != 0) return NULL;
+
+ // Search
+ LOCK( &srv->lConnections );
+ for(conn = srv->Connections;
+ conn && conn->Node.ImplInt != id;
+ conn = conn->Next);
+ RELEASE( &srv->lConnections );
+
+ // If not found, ret NULL
+ if(!conn) return NULL;
+
+ // Return node
+ return &conn->Node;
+}
+
+/**
+ * \brief Handle IOCtl calls
+ */
+int TCP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ tTCPListener *srv = Node->ImplPtr;
+
+ switch(ID)
+ {
+ case 4: // Get/Set Port
+ if(!Data) // Get Port
+ return srv->Port;
+
+ if(srv->Port) // Wait, you can't CHANGE the port
+ return -1;
+
+ if(!CheckMem(Data, sizeof(Uint16))) // Sanity check
+ return -1;
+
+ // Permissions check
+ if(Threads_GetUID() != 0
+ && *(Uint16*)Data != 0
+ && *(Uint16*)Data < 1024)
+ return -1;
+
+ // TODO: Check if a port is in use
+
+ // Set Port
+ srv->Port = *(Uint16*)Data;
+ if(srv->Port == 0) // Allocate a random port
+ srv->Port = TCP_GetUnusedPort();
+ else // Else, mark this as used
+ TCP_AllocatePort(srv->Port);
+
+ Log_Log("TCP", "Server %p listening on port %i", srv, srv->Port);
+
+ return srv->Port;
+ }
+ return 0;
+}
+
+void TCP_Server_Close(tVFS_Node *Node)
+{
+ free(Node->ImplPtr);
+}
+
+// --- Client
+/**
+ * \brief Create a client node
+ */
+tVFS_Node *TCP_Client_Init(tInterface *Interface)
+{
+ tTCPConnection *conn = malloc( sizeof(tTCPConnection) );
+
+ conn->State = TCP_ST_CLOSED;
+ conn->Interface = Interface;
+ conn->LocalPort = 0;
+ conn->RemotePort = 0;
+ memset( &conn->RemoteIP, 0, sizeof(conn->RemoteIP) );
+
+ conn->Node.ImplPtr = conn;
+ conn->Node.NumACLs = 1;
+ conn->Node.ACLs = &gVFS_ACL_EveryoneRW;
+ conn->Node.Read = TCP_Client_Read;
+ conn->Node.Write = TCP_Client_Write;
+ conn->Node.IOCtl = TCP_Client_IOCtl;
+ conn->Node.Close = TCP_Client_Close;
+
+ LOCK(&glTCP_OutbountCons);
+ conn->Next = gTCP_OutbountCons;
+ gTCP_OutbountCons = conn;
+ RELEASE(&glTCP_OutbountCons);
+
+ return &conn->Node;
+}
+
+/**
+ * \brief Wait for a packet and return it
+ * \note If \a Length is smaller than the size of the packet, the rest
+ * of the packet's data will be discarded.
+ */
+Uint64 TCP_Client_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ tTCPConnection *conn = Node->ImplPtr;
+ tTCPStoredPacket *pkt;
+
+ Log("TCP_Client_Read: (Length=%i)", Length);
+
+ // Check if connection is open
+ if( conn->State != TCP_ST_OPEN ) return 0;
+
+ // Poll packets
+ for(;;)
+ {
+ // Lock list and check if there is a packet
+ LOCK( &conn->lRecievedPackets );
+ if( conn->RecievedPackets == NULL ) {
+ // If not, release the lock, yield and try again
+ RELEASE( &conn->lRecievedPackets );
+ Threads_Yield();
+ continue;
+ }
+
+ // Get packet pointer
+ pkt = conn->RecievedPackets;
+ conn->RecievedPackets = pkt->Next;
+ // Release the lock (we don't need it any more)
+ RELEASE( &conn->lRecievedPackets );
+
+ Log("TCP_Client_Read: pkt->Length = %i", pkt->Length);
+
+ // Copy Data
+ if(Length > pkt->Length) Length = pkt->Length;
+ memcpy(Buffer, pkt->Data, Length);
+
+ // Free packet and return
+ free(pkt);
+ return Length;
+ }
+}
+
+Uint64 TCP_Client_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+ return 0;
+}
+
+/**
+ * \brief Control a client socket
+ */
+int TCP_Client_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+ tTCPConnection *conn = Node->ImplPtr;
+
+ switch(ID)
+ {
+ case 4: // Get/Set local port
+ if(!Data)
+ return conn->LocalPort;
+ if(conn->State != TCP_ST_CLOSED)
+ return -1;
+ if(!CheckMem(Data, sizeof(Uint16)))
+ return -1;
+
+ if(Threads_GetUID() != 0 && *(Uint16*)Data < 1024)
+ return -1;
+
+ conn->LocalPort = *(Uint16*)Data;
+ return 0;
+
+ case 5: // Get/Set remote port
+ if(!Data) return conn->RemotePort;
+ if(conn->State != TCP_ST_CLOSED) return -1;
+ if(!CheckMem(Data, sizeof(Uint16))) return -1;
+ conn->RemotePort = *(Uint16*)Data;
+ return 0;
+
+ case 6: // Set Remote IP
+ if( conn->State != TCP_ST_CLOSED )
+ return -1;
+ if( conn->Interface->Type == 4 )
+ {
+ if(!CheckMem(Data, sizeof(tIPv4))) return -1;
+ conn->RemoteIP.v4 = *(tIPv4*)Data;
+ }
+ else if( conn->Interface->Type == 6 )
+ {
+ if(!CheckMem(Data, sizeof(tIPv6))) return -1;
+ conn->RemoteIP.v6 = *(tIPv6*)Data;
+ }
+ return 0;
+
+ case 7: // Connect
+ if(conn->LocalPort == -1)
+ conn->LocalPort = TCP_GetUnusedPort();
+ if(conn->RemotePort == -1)
+ return 0;
+
+ TCP_StartConnection(conn);
+ return 1;
+ }
+
+ return 0;
+}
+
+void TCP_Client_Close(tVFS_Node *Node)
+{
+ free(Node->ImplPtr);
+}