diff --git a/Demo/Network/TcpEchoServer.HC b/Demo/Network/TcpEchoServer.HC new file mode 100644 index 00000000..081cc82e --- /dev/null +++ b/Demo/Network/TcpEchoServer.HC @@ -0,0 +1,54 @@ +// vim: set ft=c: + +#include "::/Adam/Net/Socket" + +#define PORT 8000 + +I64 TcpEchoServer() { + SocketInit(); + + I64 sock = socket(AF_INET, SOCK_STREAM); + + if (sock < 0) + return -1; + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(PORT); + addr.sin_addr.s_addr = INADDR_ANY; + + if (bind(sock, &addr, sizeof(addr)) < 0) { + close(sock); + "$FG,4$TcpEchoServer: failed to bind to port %d\n$FG$", PORT; + return -1; + } + + I64 error = listen(sock, 1); + + if (error < 0) { + "$FG,6$listen: error %d\n$FG$", error; + return -1; + } + + "$FG,2$Listening on port %d\n$FG$", PORT; + + I64 client = accept(sock, 0, 0); + + U8 buffer[2048 + 1]; + I64 count = recv(client, buffer, sizeof(buffer) - 1, 0); + + if (count <= 0) { + "$FG,6$recv: error %d\n$FG$", count; + } + else { + buffer[count] = 0; + "$FG,8$Received %d bytes:\n$FG$%s\n", count, buffer; + } + + send(client, buffer, count, 0); + + close(client); + + close(sock); + return 0; +} diff --git a/Shrine/Distro/DoDistro.diff b/Shrine/Distro/DoDistro.diff index d5ea55b6..a0953abf 100644 --- a/Shrine/Distro/DoDistro.diff +++ b/Shrine/Distro/DoDistro.diff @@ -8,7 +8,7 @@ index 70552ac..5e0ef2a 100644 //To save space, optionally delete dictionary. - //Del("/Distro/Adam/AutoComplete/ACDefs.DATA"); + Del("/Distro/Adam/AutoComplete/ACDefs.DATA"); - CopyTree("/Linux","/Distro/Linux"); //You can leave this out. + CopyTree("/Downloads","/Distro/Downloads"); //You can leave this out. DirMk("/Distro/Tmp"); DirMk("/Distro/Tmp/ScrnShots"); @@ -44,6 +44,6 @@ U0 MakeMyISO(U8 *_out_iso_filename) diff --git a/Shrine/Distro/KGlbls.diff b/Shrine/Distro/KGlbls.diff index 581fd317..b44f2d21 100644 --- a/Shrine/Distro/KGlbls.diff +++ b/Shrine/Distro/KGlbls.diff @@ -4,4 +4,4 @@ index df09c62..977d075 100644 +++ b/Patched/Kernel/KGlbls.HC @@ -16 +16 @@ F64 *pow10_I64, - sys_os_version=5.030; -+ sys_os_version=5.040; ++ sys_os_version=5.050; diff --git a/SnailNet/IPv4.HC b/SnailNet/IPv4.HC index 6b59c6f4..e467a4e6 100644 --- a/SnailNet/IPv4.HC +++ b/SnailNet/IPv4.HC @@ -231,6 +231,11 @@ I64 IPv4Handler(CEthFrame* eth_frame) { if (error < 0) return error; + // This seems necessary to receive connections under VBox NAT, + // but is also pretty slow, so should be optimized to use a better + // struct than linked list. + ArpCachePut(packet.source_ip, eth_frame->source_addr); + CL4Protocol* l4 = l4_protocols; while (l4) { diff --git a/SnailNet/Tcp.HC b/SnailNet/Tcp.HC index 9b60bae7..4027a49b 100644 --- a/SnailNet/Tcp.HC +++ b/SnailNet/Tcp.HC @@ -82,6 +82,11 @@ class CTcpSocket { I64 recv_buf_read_pos; I64 recv_buf_write_pos; + CTcpSocket* backlog_next; + CTcpSocket* backlog_first; + CTcpSocket* backlog_last; + I64 backlog_remaining; + CTcpSendBufHeader* send_buf_first; CTcpSendBufHeader* send_buf_last; @@ -308,7 +313,7 @@ static U0 TcpSocketAckSendBufs(CTcpSocket* s, U32 seg_ack) { I64 seg_ack_rel = (seg_ack - sb->seq_end) & 0xffffffff; I64 snd_nxt_rel = (s->snd_nxt - sb->seq_end) & 0xffffffff; - if (seg_ack_rel < snd_nxt_rel) { + if (seg_ack_rel <= snd_nxt_rel) { // Update smoothed RTT F64 rtt = time - sb->time_sent; s->srtt = (s->srtt * TCP_SRTT_ALPHA) + ((1.0 - TCP_SRTT_ALPHA) * rtt); @@ -369,18 +374,68 @@ static U0 TcpSocketCheckSendBufs(CTcpSocket* s) { } I64 TcpSocketAccept(CTcpSocket* s, sockaddr* addr, I64 addrlen) { - no_warn s; - no_warn addr; + if (s->state != TCP_STATE_LISTEN) + return -1; + + while (1) { + // TODO: Thread safe? + if (s->backlog_first) { + CTcpSocket* new_socket = s->backlog_first; + "Retr %p\n", new_socket; + + s->backlog_first = s->backlog_first->backlog_next; + if (!s->backlog_first) + s->backlog_last = NULL; + + s->backlog_remaining++; + + // TODO: this should be done in a way that doesn't block on accept() + I64 maxtime = cnts.jiffies + TCP_CONNECT_TIMEOUT * JIFFY_FREQ / 1000; + + while (cnts.jiffies < maxtime) { + if (new_socket->state == TCP_STATE_ESTABLISHED || new_socket->state == TCP_STATE_CLOSED) + break; + else + Yield; + } + + if (new_socket->state != TCP_STATE_ESTABLISHED) { + close(new_socket); + return -1; + } + + return new_socket; + } + else + Yield; + } + + no_warn addr; // FIXME no_warn addrlen; return -1; } I64 TcpSocketBind(CTcpSocket* s, sockaddr* addr, I64 addrlen) { - // FIXME: implement - no_warn s; - no_warn addr; - no_warn addrlen; - return -1; + if (addrlen < sizeof(sockaddr_in)) + return -1; + + if (s->state != TCP_STATE_CLOSED) + return -1; + + sockaddr_in* addr_in = addr; + + U16 local_port = ntohs(addr_in->sin_port); + + // TODO: address & stuff + if (tcp_bound_sockets[local_port] != NULL) + return -1; + + tcp_bound_sockets[local_port] = s; + + s->local_addr = IPv4GetAddress(); + s->local_port = local_port; + + return 0; } I64 TcpSocketClose(CTcpSocket* s) { @@ -388,6 +443,16 @@ I64 TcpSocketClose(CTcpSocket* s) { TcpSend2(s, TCP_FLAG_RST); } + // Free backlog + CTcpSocket* backlog = s->backlog_first; + CTcpSocket* backlog2; + + while (backlog) { + backlog2 = backlog->backlog_next; + close(backlog); + backlog = backlog2; + } + if (s->local_port) tcp_bound_sockets[s->local_port] = NULL; @@ -449,14 +514,20 @@ I64 TcpSocketConnect(CTcpSocket* s, sockaddr* addr, I64 addrlen) { } I64 TcpSocketListen(CTcpSocket* s, I64 backlog) { - no_warn s; - no_warn backlog; - return -1; + if (s->state != TCP_STATE_CLOSED) + return -1; + + // Enter listen state. If a SYN packet arrives, it will be processed by TcpHandler, + // which opens the connection and puts the new socket into the listening socket's accept backlog. + s->state = TCP_STATE_LISTEN; + s->backlog_remaining = backlog; + + return 0; } I64 TcpSocketRecvfrom(CTcpSocket* s, U8* buf, I64 len, I64 flags, sockaddr* src_addr, I64 addrlen) { no_warn flags; - no_warn src_addr; + no_warn src_addr; // FIXME no_warn addrlen; //"TcpSocketRecvfrom\n"; while (s->state == TCP_STATE_ESTABLISHED && s->recv_buf_read_pos == s->recv_buf_write_pos) { @@ -590,6 +661,11 @@ CTcpSocket* TcpSocket(U16 domain, U16 type) { s->recv_buf_read_pos = 0; s->recv_buf_write_pos = 0; + s->backlog_next = NULL; + s->backlog_first = NULL; + s->backlog_last = NULL; + s->backlog_remaining = 0; + /*s->rcvtimeo_ms = 0; s->recv_maxtime = 0; @@ -601,9 +677,6 @@ CTcpSocket* TcpSocket(U16 domain, U16 type) { } U0 TcpSocketHandle(CTcpSocket* s, CIPv4Packet* packet, CTcpHeader* hdr, U8* data, I64 length) { - if (s->state == TCP_STATE_CLOSED) - return; - U32 seg_len = length; if (hdr->flags & TCP_FLAG_FIN) seg_len++; @@ -611,6 +684,56 @@ U0 TcpSocketHandle(CTcpSocket* s, CIPv4Packet* packet, CTcpHeader* hdr, U8* data U32 seg_seq = ntohl(hdr->seq); + if (s->state == TCP_STATE_LISTEN) { + // A new connection is being opened. + + if ((hdr->flags & TCP_FLAG_SYN) && s->backlog_remaining > 0) { + //"SYN in from %08X:%d => %08X:%d.\n", packet->source_ip, ntohs(hdr->source_port), + // packet->dest_ip, ntohs(hdr->dest_port); + CTcpSocket* new_socket = TcpSocket(AF_INET, SOCK_STREAM); + + new_socket->local_addr = IPv4GetAddress(); + new_socket->local_port = s->local_port; + new_socket->remote_addr = packet->source_ip; + new_socket->remote_port = ntohs(hdr->source_port); + + new_socket->snd_una = 0; + new_socket->snd_nxt = 0; + new_socket->snd_wnd = 0; + new_socket->mss = TCP_DEFAULT_MSS; + + new_socket->rcv_nxt = ++seg_seq; + new_socket->rcv_wnd= TCP_WINDOW_SIZE; + + new_socket->conntime = tS; + + TcpSend2(new_socket, TCP_FLAG_SYN | TCP_FLAG_ACK); + new_socket->state = TCP_STATE_SYN_RECEIVED; + + // FIXME FIXME FIXME FIXME + tcp_bound_sockets[new_socket->local_port] = new_socket; + + if (s->backlog_last) + s->backlog_last->backlog_next = new_socket; + else + s->backlog_first = new_socket; + + s->backlog_last = new_socket; + s->backlog_remaining--; + } + else { + //"REJ %08X:%d (as %08X:%d)\n", packet->source_ip, ntohs(hdr->source_port), + // packet->dest_ip, ntohs(hdr->dest_port); + TcpSend(packet->dest_ip, ntohs(hdr->dest_port), packet->source_ip, ntohs(hdr->source_port), + seg_seq + 1, seg_seq + 1, TCP_FLAG_ACK | TCP_FLAG_RST); + } + + return; + } + + if (s->state == TCP_STATE_CLOSED) + return; + Bool must_ack = FALSE; // Process SYN @@ -666,6 +789,12 @@ U0 TcpSocketHandle(CTcpSocket* s, CIPv4Packet* packet, CTcpHeader* hdr, U8* data s->srtt = tS - s->conntime; //"Initial RTT: %f ms", s->srtt * 1000; } + else if (s->state == TCP_STATE_SYN_RECEIVED) { + //"Connection established.\n"; + s->state = TCP_STATE_ESTABLISHED; + s->srtt = tS - s->conntime; + //"Initial RTT: %f ms", s->srtt * 1000; + } } else { // Unacceptable ACK @@ -674,8 +803,8 @@ U0 TcpSocketHandle(CTcpSocket* s, CIPv4Packet* packet, CTcpHeader* hdr, U8* data if (s->state == TCP_STATE_LISTEN || s->state == TCP_STATE_SYN_SENT || s->state == TCP_STATE_SYN_RECEIVED) { // Reset - TcpSend(s->local_addr, s->local_port, packet->source_ip, hdr->source_port, - seg_ack, seg_seq + seg_len, TCP_FLAG_RST); + TcpSend(packet->dest_ip, ntohs(hdr->dest_port), packet->source_ip, ntohs(hdr->source_port), + seg_ack, seg_seq + seg_len, TCP_FLAG_ACK | TCP_FLAG_RST); } else if (TcpIsSynchronizedState(s->state)) { // Send a 'corrective' ACK diff --git a/TOS_Distro.ISO b/TOS_Distro.ISO index 885d3c3e..fa0625ee 100644 Binary files a/TOS_Distro.ISO and b/TOS_Distro.ISO differ diff --git a/TempleOS b/TempleOS index f75b59fc..f68cf762 160000 --- a/TempleOS +++ b/TempleOS @@ -1 +1 @@ -Subproject commit f75b59fc7580dce9a3fa263b45958b231e6a6476 +Subproject commit f68cf76270862d3544f6523b9d13b81c82fba019