From 2de1c1760fc431aa9fc8dc35653a08877f6afa9a Mon Sep 17 00:00:00 2001 From: gbeauche <> Date: Sun, 5 Dec 2004 16:48:36 +0000 Subject: [PATCH] import NAT-Router code from original Basilisk II for Windows --- BasiliskII/src/Windows/router/arp.cpp | 95 + BasiliskII/src/Windows/router/arp.h | 28 + BasiliskII/src/Windows/router/dump.cpp | 49 + BasiliskII/src/Windows/router/dump.h | 30 + BasiliskII/src/Windows/router/dynsockets.cpp | 184 ++ BasiliskII/src/Windows/router/dynsockets.h | 59 + BasiliskII/src/Windows/router/ftp.cpp | 192 ++ BasiliskII/src/Windows/router/ftp.h | 50 + BasiliskII/src/Windows/router/icmp.cpp | 220 +++ BasiliskII/src/Windows/router/icmp.h | 38 + BasiliskII/src/Windows/router/iphelp.cpp | 235 +++ BasiliskII/src/Windows/router/iphelp.h | 36 + BasiliskII/src/Windows/router/ipsocket.cpp | 265 +++ BasiliskII/src/Windows/router/ipsocket.h | 66 + .../src/Windows/router/mib/interfaces.cpp | 68 + .../src/Windows/router/mib/interfaces.h | 35 + .../src/Windows/router/mib/mibaccess.cpp | 308 ++++ BasiliskII/src/Windows/router/mib/mibaccess.h | 83 + BasiliskII/src/Windows/router/router.cpp | 204 +++ BasiliskII/src/Windows/router/router.h | 55 + BasiliskII/src/Windows/router/router_types.h | 187 ++ BasiliskII/src/Windows/router/tcp.cpp | 1606 +++++++++++++++++ BasiliskII/src/Windows/router/tcp.h | 31 + BasiliskII/src/Windows/router/udp.cpp | 205 +++ BasiliskII/src/Windows/router/udp.h | 38 + 25 files changed, 4367 insertions(+) create mode 100755 BasiliskII/src/Windows/router/arp.cpp create mode 100755 BasiliskII/src/Windows/router/arp.h create mode 100755 BasiliskII/src/Windows/router/dump.cpp create mode 100755 BasiliskII/src/Windows/router/dump.h create mode 100755 BasiliskII/src/Windows/router/dynsockets.cpp create mode 100755 BasiliskII/src/Windows/router/dynsockets.h create mode 100755 BasiliskII/src/Windows/router/ftp.cpp create mode 100755 BasiliskII/src/Windows/router/ftp.h create mode 100755 BasiliskII/src/Windows/router/icmp.cpp create mode 100755 BasiliskII/src/Windows/router/icmp.h create mode 100755 BasiliskII/src/Windows/router/iphelp.cpp create mode 100755 BasiliskII/src/Windows/router/iphelp.h create mode 100755 BasiliskII/src/Windows/router/ipsocket.cpp create mode 100755 BasiliskII/src/Windows/router/ipsocket.h create mode 100755 BasiliskII/src/Windows/router/mib/interfaces.cpp create mode 100755 BasiliskII/src/Windows/router/mib/interfaces.h create mode 100755 BasiliskII/src/Windows/router/mib/mibaccess.cpp create mode 100755 BasiliskII/src/Windows/router/mib/mibaccess.h create mode 100755 BasiliskII/src/Windows/router/router.cpp create mode 100755 BasiliskII/src/Windows/router/router.h create mode 100755 BasiliskII/src/Windows/router/router_types.h create mode 100755 BasiliskII/src/Windows/router/tcp.cpp create mode 100755 BasiliskII/src/Windows/router/tcp.h create mode 100755 BasiliskII/src/Windows/router/udp.cpp create mode 100755 BasiliskII/src/Windows/router/udp.h diff --git a/BasiliskII/src/Windows/router/arp.cpp b/BasiliskII/src/Windows/router/arp.cpp new file mode 100755 index 00000000..723302b2 --- /dev/null +++ b/BasiliskII/src/Windows/router/arp.cpp @@ -0,0 +1,95 @@ +/* + * arp.cpp - ip router + * + * Basilisk II (C) 1997-2004 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "cpu_emulation.h" +#include "prefs.h" +#include "ether_windows.h" +#include "ether.h" +#include "router.h" +#include "router_types.h" +#include "iphelp.h" +#include "arp.h" +#include "icmp.h" +#include "dump.h" + + +#if DEBUG +#pragma optimize("",off) +#endif + +#include "debug.h" + + +// ARP queries can be replied immediately. + +bool write_arp( arp_t *req, int len ) +{ + D(bug("write_arp() len=%d, htype=%d, ptype=%04x, opcode=%d, halen=%d, palen=%d\r\n",len, ntohs(req->htype), ntohs(req->ptype), ntohs(req->opcode), req->halen, req->palen)); + + start_icmp_listen(); + + bool result = false; + + if( len >= sizeof(arp_t) && + req->htype == htons(arp_hwtype_enet) && + req->ptype == htons(mac_type_ip4) && + req->opcode == htons(arp_request) && + req->halen == 6 && + req->palen == 4 + ) + { + if(memcmp( req->srcp, req->dstp, 4 ) == 0) { + // No reply. MacOS is making sure that there are no duplicate ip addresses. + // Update localhost (==Mac) ip address (needed by incoming icmp) + macos_ip_address = ntohl( *((uint32 *)&req->srcp[0]) ); + D(bug("Mac ip: %02x %02x %02x %02x\r\n", req->srcp[0], req->srcp[1], req->srcp[2], req->srcp[3])); + } else { + arp_t arp; + + D(bug("Source NIC: %02x %02x %02x %02x\r\n", req->srcp[0], req->srcp[1], req->srcp[2], req->srcp[3])); + D(bug("Dest NIC: %02x %02x %02x %02x\r\n", req->dstp[0], req->dstp[1], req->dstp[2], req->dstp[3])); + + // memcpy( arp.mac.dest, req->mac.src, 6 ); + memcpy( arp.mac.dest, ether_addr, 6 ); + memcpy( arp.mac.src, router_mac_addr, 6 ); + arp.mac.type = htons(mac_type_arp); + arp.htype = htons(arp_hwtype_enet); + arp.ptype = htons(mac_type_ip4); + arp.halen = 6; + arp.palen = 4; + arp.opcode = htons(arp_reply); + memcpy( arp.srch, router_mac_addr, 6 ); + memcpy( arp.srcp, req->dstp, 4 ); + // memcpy( arp.dsth, req->srch, 6 ); + memcpy( arp.dsth, ether_addr, 6 ); + memcpy( arp.dstp, req->srcp, 4 ); + + // Update here, too, just in case. + macos_ip_address = ntohl( *((uint32 *)&req->srcp[0]) ); + + enqueue_packet( (uint8 *)&arp, sizeof(arp) ); + } + result = true; + } + return result; +} diff --git a/BasiliskII/src/Windows/router/arp.h b/BasiliskII/src/Windows/router/arp.h new file mode 100755 index 00000000..885aa430 --- /dev/null +++ b/BasiliskII/src/Windows/router/arp.h @@ -0,0 +1,28 @@ +/* + * arp.h - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ARP_H_ +#define _ARP_H_ + +bool write_arp( arp_t *req, int len ); + +#endif // _ARP_H_ diff --git a/BasiliskII/src/Windows/router/dump.cpp b/BasiliskII/src/Windows/router/dump.cpp new file mode 100755 index 00000000..d33aad34 --- /dev/null +++ b/BasiliskII/src/Windows/router/dump.cpp @@ -0,0 +1,49 @@ +/* + * dump.cpp - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "dump.h" + +#if DEBUG +#pragma optimize("",off) +#endif + +#include "debug.h" + + +void dump_bytes( uint8 *packet, int length ) +{ +#if DEBUG + char buf[1000], sm[10]; + + *buf = 0; + + if(length > 256) length = 256; + + for (int i=0; i +#include "dump.h" +#include "prefs.h" +#include "ftp.h" + +#if DEBUG +#pragma optimize("",off) +#endif + +#include "debug.h" + +static int m_ftp_port_count = 0; +#define MAX_FTP_PORTS 100 +static uint16 m_ftp_ports[MAX_FTP_PORTS]; + +bool ftp_is_ftp_port( uint16 port ) +{ + for( int i=0; i> 24, + (ip >> 16) & 0xFF, + (ip >> 8) & 0xFF, + ip & 0xFF, + (port >> 8) & 0xFF, + port & 0xFF, + 0x0d, 0x0a + ); + + count = strlen(buf); + + D(bug("ftp_modify_port_command: \"%s\"\r\n", buf )); +} + +// this should be robust. rather skip it than do anything dangerous. +void ftp_parse_port_command( + char *buf, + uint32 count, + uint16 &ftp_data_port, + bool is_pasv +) +{ + ftp_data_port = 0; + + if( !count ) return; + + uint8 b[100]; + uint32 ftp_ip = 0; + + // make it a c-string + if( count >= sizeof(b) ) count = sizeof(b)-1; + memcpy( b, buf, count ); + b[ count ] = 0; + + for( uint32 i=0; i 'z' ) { + b[i] = ' '; + } else { + b[i] = tolower(b[i]); + } + } + + // D(bug("FTP: \"%s\"\r\n", b )); + + char *s = (char *)b; + + while( *s == ' ' ) s++; + + if(is_pasv) { + /* + LOCAL SERVER: ..227 Entering Passive Mode (192,168,0,2,6,236). 0d 0a + */ + if( atoi(s) == 227 && strstr(s,"passive") ) { + while( *s && *s != '(' ) s++; + if( *s++ == 0 ) s = 0; + } else { + s = 0; + } + } else { + /* + LOCAL CLIENT: PORT 192,168,0,1,14,147 0d 0a + */ + if( strncmp(s,"port ",5) == 0 ) { + s += 5; + } else { + s = 0; + } + } + + if(s && *s) { + // get remote ip (used only for verification) + for( uint32 i=0; i<4; i++ ) { + while( *s == ' ' ) s++; + if(!isdigit(*s)) { + ftp_ip = 0; + break; + } + ftp_ip = (ftp_ip << 8) + atoi(s); + while( *s && *s != ',' ) s++; + if(!*s) { + ftp_ip = 0; + break; + } + s++; + } + + if(ftp_ip) { + // get local port + for( uint32 i=0; i<2; i++ ) { + while( *s == ' ' ) s++; + if(!isdigit(*s)) { + ftp_data_port = 0; + break; + } + ftp_data_port = (ftp_data_port << 8) + atoi(s); + while( *s && *s != ',' && *s != ')' ) s++; + if(!*s) + break; + else + s++; + } + } + } + if(ftp_data_port) { + D(bug("ftp_parse_port_command: \"%s\"; port is %d\r\n", b, ftp_data_port )); + } +} diff --git a/BasiliskII/src/Windows/router/ftp.h b/BasiliskII/src/Windows/router/ftp.h new file mode 100755 index 00000000..c008df0e --- /dev/null +++ b/BasiliskII/src/Windows/router/ftp.h @@ -0,0 +1,50 @@ +/* + * ftp.h - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _FTP_H_ +#define _FTP_H_ + +// Read the preferences. +void init_ftp(); + +// Compares against a list provided by the user. +bool ftp_is_ftp_port( uint16 port ); + +// Determine whether this is a ftp client PORT command or ftp server entering to passive mode. +void ftp_parse_port_command( + char *buf, + uint32 count, + uint16 &ftp_data_port, + bool is_pasv +); + +// Build a new command using ip and port. +void ftp_modify_port_command( + char *buf, + int &count, + const uint32 max_size, + const uint32 ip, + const uint16 port, + const bool is_pasv +); + +#endif // _FTP_H_ diff --git a/BasiliskII/src/Windows/router/icmp.cpp b/BasiliskII/src/Windows/router/icmp.cpp new file mode 100755 index 00000000..c283b6da --- /dev/null +++ b/BasiliskII/src/Windows/router/icmp.cpp @@ -0,0 +1,220 @@ +/* + * icmp.cpp - ip router + * + * Basilisk II (C) 1997-2004 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "cpu_emulation.h" +#include "ws2tcpip.h" +#include "prefs.h" +#include "ether_windows.h" +#include "ether.h" +#include "router.h" +#include "router_types.h" +#include "dynsockets.h" +#include "ipsocket.h" +#include "iphelp.h" +#include "icmp.h" +#include "dump.h" + + +#if DEBUG +#pragma optimize("",off) +#endif + +#include "debug.h" + + +// Saved for cleanup. +static socket_t *icmp_incoming_s = 0; + + +void stop_icmp_listen() +{ + if(icmp_incoming_s) { + delete icmp_incoming_s; + icmp_incoming_s = 0; + } +} + +void start_icmp_listen() +{ + if(!icmp_incoming_s) { + icmp_incoming_s = new socket_t(IPPROTO_ICMP); + + icmp_incoming_s->permanent = TRUE; + icmp_incoming_s->s = _socket( AF_INET, SOCK_RAW, IPPROTO_ICMP ); + + memset( &icmp_incoming_s->from, 0, icmp_incoming_s->from_len ); + icmp_incoming_s->from.sin_family = AF_INET; + + if(icmp_incoming_s->s == INVALID_SOCKET) { + D(bug("Failed to create icmp listening socket (NT/no admin?)\r\n" )); + delete icmp_incoming_s; + icmp_incoming_s = 0; + } else { + D(bug("icmp listening socket created\r\n" )); + raw_sockets_available = true; + struct sockaddr_in to; + memset( &to, 0, sizeof(to) ); + to.sin_family = AF_INET; + if( _bind ( icmp_incoming_s->s, (const struct sockaddr *)&to, sizeof(to) ) == SOCKET_ERROR ) { + D(bug("Listening to inbound icmp failed, error code = %d\r\n", _WSAGetLastError() )); + _closesocket( icmp_incoming_s->s ); + delete icmp_incoming_s; + icmp_incoming_s = 0; + } else { + D(bug("icmp listening socket bound\r\n" )); + if(!icmp_incoming_s->b_recfrom()) { + D(bug("b_recfrom() from inbound icmp failed, error code = %d\r\n", _WSAGetLastError() )); + // _closesocket( icmp_incoming_s->s ); + // delete icmp_incoming_s; + // icmp_incoming_s = 0; + } + } + } + } +} + +void CALLBACK icmp_read_completion( + DWORD error, + DWORD bytes_read, + LPWSAOVERLAPPED lpOverlapped, + DWORD flags +) +{ + D(bug("icmp_read_completion(error=0x%x, bytes_read=%d, flags=0x%x)\r\n", error, bytes_read, flags)); + + socket_t *cmpl = (socket_t *)lpOverlapped->hEvent; + + if(error == 0 && macos_ip_address != 0) { + if(bytes_read > 1460) { + D(bug("discarding oversized icmp packet, size = \r\n", bytes_read)); + } else { + int icmp_size = sizeof(mac_t) + bytes_read; + icmp_t *icmp = (icmp_t *)malloc( icmp_size ); + if(icmp) { + mac_t *mac = (mac_t *)icmp; + ip_t *ip = (ip_t *)icmp; + + memcpy( mac->dest, ether_addr, 6 ); + memcpy( mac->src, router_mac_addr, 6 ); + mac->type = htons(mac_type_ip4); + + // Copy payload (used by ICMP checksum) + memcpy( (char *)icmp + sizeof(mac_t), cmpl->buffers[0].buf, bytes_read ); + + switch( icmp->type ) { + // May need to patch the returned ip header. + case icmp_Destination_unreachable: + case icmp_Source_quench: + case icmp_Redirect: + case icmp_Time_exceeded: + case icmp_Parameter_problem: + ip_t *ip_if = (ip_t *)( (char *)icmp + sizeof(icmp_t) + sizeof(uint32) - sizeof(mac_t) ); + + // This would be needed (traceroute) + // ip_if->ident = ??; + + // Cannot fix some fields, this should be enough: + ip_if->src = htonl(macos_ip_address); + + if(ip_if->proto == ip_proto_udp) { + udp_t *udp_if = (udp_t *)ip_if; + // udp_if->src_port = ... don't know!; + } else if(ip_if->proto == ip_proto_tcp) { + tcp_t *tcp_if = (tcp_t *)ip_if; + // tcp_if->src_port = ... don't know!; + } + break; + } + + make_icmp_checksum( icmp, icmp_size ); + + // Replace the target ip address + ip->dest = htonl(macos_ip_address); + ip->ttl--; + make_ip4_checksum( ip ); + + dump_bytes( (uint8 *)icmp, icmp_size ); + + if( ip->ttl == 0 ) { + D(bug("icmp packet ttl expired\r\n")); + } else { + enqueue_packet( (uint8 *)icmp, icmp_size ); + } + free(icmp); + } + } + } + + memset( &cmpl->from, 0, cmpl->from_len ); + + if(is_router_shutting_down) { + delete cmpl; + } else if(cmpl->s == INVALID_SOCKET || !cmpl->b_recfrom()) { + // delete cmpl; + } +} + +void write_icmp( icmp_t *icmp, int len ) +{ + struct in_addr ia; + ia.s_addr = icmp->ip.dest; + D(bug("write_icmp(%s)\r\n", _inet_ntoa(ia) )); + + if(!raw_sockets_available) { + D(bug("write_icmp() cannot proceed, raw sockets not available\r\n" )); + return; + } + + if(len < sizeof(icmp_t)) { + D(bug("Too small icmp packet(%d), dropped\r\n", len)); + return; + } + + // must be updated, ttl changed + make_icmp_checksum( icmp, len ); + + SOCKET s = _socket( AF_INET, SOCK_RAW, IPPROTO_ICMP ); + if(s != INVALID_SOCKET) { + struct sockaddr_in to; + memset( &to, 0, sizeof(to) ); + to.sin_family = AF_INET; + to.sin_addr.s_addr = icmp->ip.dest; + + char *data = (char *)icmp + sizeof(ip_t); + int dlen = len - sizeof(ip_t); + + int ttl = icmp->ip.ttl; + if(_setsockopt( s, IPPROTO_IP, IP_TTL, (const char *)&ttl, sizeof(int) ) == SOCKET_ERROR ) { + D(bug("could not set ttl to %d.\r\n", ttl)); + } else { + D(bug("ttl set to %d.\r\n", ttl)); + } + + if(SOCKET_ERROR == _sendto( s, data, dlen, 0, (struct sockaddr *)&to, sizeof(to) )) { + D(bug("Failed to send icmp via raw socket\r\n" )); + } + _closesocket(s); + } else { + D(bug("Could not create raw socket for icmp\r\n" )); + } +} diff --git a/BasiliskII/src/Windows/router/icmp.h b/BasiliskII/src/Windows/router/icmp.h new file mode 100755 index 00000000..11bb71fd --- /dev/null +++ b/BasiliskII/src/Windows/router/icmp.h @@ -0,0 +1,38 @@ +/* + * icmp.h - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ICMP_H_ +#define _ICMP_H_ + +void start_icmp_listen(); +void stop_icmp_listen(); + +void write_icmp( icmp_t *icmp, int len ); + +void CALLBACK icmp_read_completion( + DWORD error, + DWORD bytes_read, + LPWSAOVERLAPPED lpOverlapped, + DWORD flags +); + +#endif // _ICMP_H_ diff --git a/BasiliskII/src/Windows/router/iphelp.cpp b/BasiliskII/src/Windows/router/iphelp.cpp new file mode 100755 index 00000000..769efd44 --- /dev/null +++ b/BasiliskII/src/Windows/router/iphelp.cpp @@ -0,0 +1,235 @@ +/* + * iphelp.cpp - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "cpu_emulation.h" +#include "ether_windows.h" +#include "ether.h" +#include "router.h" +#include "router_types.h" +#include "tcp.h" +#include "icmp.h" +#include "udp.h" +#include "iphelp.h" +#include "dump.h" + + +#if DEBUG +#pragma optimize("",off) +#endif + +#include "debug.h" + + +void make_icmp_checksum( icmp_t *icmp, int len ) +{ + icmp->checksum = 0; + + uint16 sz = (len-sizeof(ip_t))/2; + uint16 *p = (uint16 *)( (uint8 *)icmp + sizeof(ip_t) ); + + uint32 sum32 = 0; + for( int i=0; ichecksum = htons((uint16)sum32); +} + +void make_tcp_checksum( tcp_t *tcp, int len ) +{ + tcp->checksum = 0; + + int tcp_len = len - sizeof(ip_t); + uint16 sz = tcp_len/2; + uint16 *p = (uint16 *)( (uint8 *)tcp + sizeof(ip_t) ); + + uint32 sum32 = 0; + for( int i=0; iip.src)); + pseudo.src_hi = HIWORD(ntohl(tcp->ip.src)); + pseudo.dest_lo = LOWORD(ntohl(tcp->ip.dest)); + pseudo.dest_hi = HIWORD(ntohl(tcp->ip.dest)); + pseudo.proto = (uint16)tcp->ip.proto; + pseudo.msg_len = tcp->header_len >> 2; + + int datalen = len - sizeof(tcp_t); + pseudo.msg_len += datalen; + + p = (uint16 *)&pseudo; + + for( int i=0; ichecksum = htons((uint16)sum32); +} + +void make_ip4_checksum( ip_t *ip ) +{ + ip->checksum = 0; + uint16 sz = ip->header_len * 2; + uint16 *p = (uint16 *)( (uint8 *)ip + sizeof(mac_t) ); + + uint32 sum32 = 0; + for( int i=0; ichecksum = htons((uint16)sum32); +} + +void make_udp_checksum( udp_t *udp ) +{ + udp->checksum = 0; + return; + + // UDP checksums are optional. + + /* + uint16 sz = ntohs(udp->msg_len) / 2; + uint16 *p = (uint16 *)( (uint8 *)udp + sizeof(ip_t) ); + + uint32 sum32 = 0; + for( int i=0; iip.src)); + pseudo.src_hi = HIWORD(ntohl(udp->ip.src)); + pseudo.dest_lo = LOWORD(ntohl(udp->ip.dest)); + pseudo.dest_hi = HIWORD(ntohl(udp->ip.dest)); + pseudo.proto = (uint16)udp->ip.proto; + pseudo.msg_len = ntohs(udp->msg_len); // ??? + + p = (uint16 *)&pseudo; + + for( i=0; ichecksum = htons((uint16)sum32); + */ +} + +void error_winsock_2_icmp( int err, ip_t *ip_err, int dlen_err ) +{ + int type = -1, code = -1, msg_size = 0; + + switch( err ) { + case WSAEHOSTUNREACH: + case WSAETIMEDOUT: + type = icmp_Destination_unreachable; + code = 1; // Host unreachable + msg_size = (ip_err->header_len << 2) + 4 + 8; // ip header + unused + 64 msg bits + break; + case WSAENETDOWN: + case WSAENETUNREACH: + type = icmp_Destination_unreachable; + code = 0; // Network unreachable + msg_size = (ip_err->header_len << 2) + 4 + 8; // ip header + unused + 64 msg bits + break; + case WSAETTLEXCEEDED: + type = icmp_Time_exceeded; + code = 0; // Transit TTL exceeded + msg_size = (ip_err->header_len << 2) + 4 + 8; // ip header + unused + 64 msg bits + break; + } + + if(type >= 0 && macos_ip_address != 0) { + D(bug("sending icmp error reply. type=%d, code=%d, msg_size=%d\r\n", type, code, msg_size)); + + int icmp_size = sizeof(icmp_t) + msg_size; + + icmp_t *icmp = (icmp_t *)malloc( icmp_size ); + if(icmp) { + mac_t *mac = (mac_t *)icmp; + ip_t *ip = (ip_t *)icmp; + + memcpy( mac->dest, ether_addr, 6 ); + memcpy( mac->src, router_mac_addr, 6 ); + mac->type = htons(mac_type_ip4); + + ip->version = 4; + ip->header_len = 5; + ip->tos = 0; + ip->total_len = htons(sizeof(icmp_t) - sizeof(mac_t) + msg_size); + + ip->ident = htons(next_ip_ident_number++); + ip->flags_n_frag_offset = 0; + ip->ttl = 128; + ip->proto = ip_proto_icmp; + ip->src = htonl(router_ip_address); + ip->dest = htonl(macos_ip_address); + make_ip4_checksum( ip ); + + icmp->type = type; + icmp->code = code; + + // zero out the unused field + memset( (char *)icmp + sizeof(icmp_t), 0, sizeof(uint32) ); + + // copy 64 bits of original message + memcpy( + (char *)icmp + sizeof(icmp_t) + sizeof(uint32), + (char *)ip_err + sizeof(mac_t), + msg_size + ); + + make_icmp_checksum( icmp, icmp_size ); + + dump_bytes( (uint8 *)icmp, icmp_size ); + + enqueue_packet( (uint8 *)icmp, icmp_size ); + free(icmp); + } + } +} diff --git a/BasiliskII/src/Windows/router/iphelp.h b/BasiliskII/src/Windows/router/iphelp.h new file mode 100755 index 00000000..6af59a8f --- /dev/null +++ b/BasiliskII/src/Windows/router/iphelp.h @@ -0,0 +1,36 @@ +/* + * iphelp.h - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _IPHELP_H_ +#define _IPHELP_H_ + +// Fake ttl exceeded code. +#define WSAETTLEXCEEDED (WSABASEERR + 1999 + 17) + +void error_winsock_2_icmp( int err, ip_t *ip_err, int dlen_err ); + +void make_icmp_checksum( icmp_t *icmp, int len ); +void make_ip4_checksum( ip_t *ip ); +void make_udp_checksum( udp_t *udp ); +void make_tcp_checksum( tcp_t *tcp, int len ); + +#endif // _IPHELP_H_ diff --git a/BasiliskII/src/Windows/router/ipsocket.cpp b/BasiliskII/src/Windows/router/ipsocket.cpp new file mode 100755 index 00000000..060fd9dc --- /dev/null +++ b/BasiliskII/src/Windows/router/ipsocket.cpp @@ -0,0 +1,265 @@ +/* + * ipsocket.cpp - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "cpu_emulation.h" +#include "ws2tcpip.h" +#include "prefs.h" +#include "ether_windows.h" +#include "ether.h" +#include "router.h" +#include "router_types.h" +#include "dynsockets.h" +#include "ipsocket.h" +#include "icmp.h" +#include "tcp.h" +#include "udp.h" +#include "dump.h" + + +#if DEBUG +#pragma optimize("",off) +#endif + +#include "debug.h" + + +socket_t::socket_t( int _proto ) +{ + s = INVALID_SOCKET; + proto = _proto; + + ip_src = ip_dest = 0; + src_port = dest_port = 0; + + memset( &overlapped, 0, sizeof(overlapped) ); + overlapped.hEvent = (HANDLE)this; + + bytes_received = 0; + flags = 0; + from_len = sizeof(struct sockaddr_in); + memset( &from, 0, sizeof(from) ); + from.sin_family = AF_INET; + + buffer_count = 1; + buffers[0].len = 1460; + buffers[0].buf = new char [buffers[0].len]; + + out_buffers[0].len = 1460; + out_buffers[0].buf = new char [out_buffers[0].len]; + + socket_ttl = GetTickCount() + 60000L; + permanent = false; +} + +socket_t::~socket_t() +{ + if(s != INVALID_SOCKET) { + _closesocket( s ); // slam! + s = INVALID_SOCKET; + } + delete [] out_buffers[0].buf; + delete [] buffers[0].buf; +} + +int socket_t::WSARecvFrom() +{ + return _WSARecvFrom( + s, + buffers, + buffer_count, + &bytes_received, + &flags, + (struct sockaddr *)&from, + &from_len, + &overlapped, + proto == IPPROTO_UDP ? udp_read_completion : icmp_read_completion + ); +} + +bool socket_t::b_recfrom() +{ + bool result; + + int ret = WSARecvFrom(); + + if(ret == SOCKET_ERROR) { + int socket_error = _WSAGetLastError(); + if(socket_error == WSA_IO_PENDING) { + D(bug("WSARecvFrom() i/o pending\r\n")); + result = true; + } else { + D(bug("_WSAGetLastError() returned %d\r\n", socket_error)); + result = false; + } + } else /*if(ret == 0) */ { + D(bug("WSARecvFrom() ok\r\n")); + // Completion routine call is already scheduled. + result = true; + } + return result; +} + +void socket_t::set_ttl( uint8 ttl ) +{ + int _ttl = ttl; // defensive programming, I know VCx + + if(_setsockopt( s, IPPROTO_IP, IP_TTL, (const char *)&_ttl, sizeof(int) ) == SOCKET_ERROR ) { + D(bug("could not set ttl to %d.\r\n", ttl)); + } else { + D(bug("ttl set to %d.\r\n", ttl)); + } +} + + +#define MAX_OPEN_SOCKETS 1024 +static socket_t *all_sockets[MAX_OPEN_SOCKETS]; +static int open_sockets = 0; + +int get_socket_index( uint16 src_port, uint16 dest_port, int proto ) +{ + int result = -1; + for( int i=0; isrc_port == src_port && cmpl->dest_port == dest_port && cmpl->proto == proto ) { + result = i; + break; + } + } + return result; +} + +int get_socket_index( uint16 src_port, int proto ) +{ + int result = -1; + for( int i=0; isrc_port == src_port && cmpl->proto == proto ) { + result = i; + break; + } + } + return result; +} + +int get_socket_index( socket_t *cmpl ) +{ + int result = -1; + for( int i=0; isrc_port, cmpl->dest_port)); + + EnterCriticalSection( &router_section ); + int i = get_socket_index( cmpl ); + if( i >= 0 ) { + delete all_sockets[i]; + all_sockets[i] = all_sockets[--open_sockets]; + } else { + D(bug("Deleted socket not in table!\r\n")); + // delete cmpl; + } + LeaveCriticalSection( &router_section ); +} + +socket_t *find_socket( uint16 src_port, uint16 dest_port, int proto ) +{ + socket_t *result = 0; + EnterCriticalSection( &router_section ); + int i = get_socket_index( src_port, dest_port, proto ); + if( i >= 0 ) { + result = all_sockets[i]; + } else { + i = get_socket_index( src_port, proto ); + if( i >= 0 ) { + delete_socket( all_sockets[i] ); + } + } + LeaveCriticalSection( &router_section ); + + D(bug("find_socket(%d,%d): %s\r\n", src_port, dest_port, result ? "found" : "not found")); + + return result; +} + +void add_socket( socket_t *cmpl ) +{ + D(bug("adding socket(%d,%d)\r\n", cmpl->src_port, cmpl->dest_port)); + + EnterCriticalSection( &router_section ); + if( open_sockets < MAX_OPEN_SOCKETS ) { + all_sockets[open_sockets++] = cmpl; + } else { + // Urgchiyuppijee! (that's finnish language, meaning "do something about this") + delete all_sockets[0]; + all_sockets[0] = cmpl; + } + LeaveCriticalSection( &router_section ); +} + +void close_old_sockets() +{ + DWORD now = GetTickCount(); + + EnterCriticalSection( &router_section ); + for( int i=open_sockets-1; i>=0; i-- ) { + socket_t *cmpl = all_sockets[i]; + if( !cmpl->permanent && now >= cmpl->socket_ttl ) { + D(bug("expiring socket(%d,%d)\r\n", cmpl->src_port, cmpl->dest_port)); + if(cmpl->s == INVALID_SOCKET) { + delete all_sockets[i]; + all_sockets[i] = all_sockets[--open_sockets]; + } else { + // read completion will deallocate + _closesocket( cmpl->s ); + } + } + } + LeaveCriticalSection( &router_section ); +} + +void close_all_sockets() +{ + D(bug("closing all(%d) sockets\r\n", open_sockets)); + + EnterCriticalSection( &router_section ); + for( int i=0; isrc_port, cmpl->dest_port)); + if(cmpl->s == INVALID_SOCKET) { + delete all_sockets[i]; + all_sockets[i] = all_sockets[--open_sockets]; + } else { + // read completion will deallocate + _closesocket( cmpl->s ); + } + } + LeaveCriticalSection( &router_section ); +} diff --git a/BasiliskII/src/Windows/router/ipsocket.h b/BasiliskII/src/Windows/router/ipsocket.h new file mode 100755 index 00000000..dd3dff54 --- /dev/null +++ b/BasiliskII/src/Windows/router/ipsocket.h @@ -0,0 +1,66 @@ +/* + * ipsocket.h - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _IPSOCKET_H_ +#define _IPSOCKET_H_ + +class socket_t { +public: + socket_t( int _proto ); + ~socket_t(); + bool b_recfrom(); + void set_ttl( uint8 ttl ); + +protected: + int WSARecvFrom(); + +public: + SOCKET s; // Always a valid socket + BOOL permanent; // T: a user-defined listening socket, + int proto; // udp/icmp + WSABUF buffers[1]; + WSABUF out_buffers[1]; + DWORD buffer_count; + DWORD bytes_received; + DWORD flags; + struct sockaddr_in from; + int from_len; + WSAOVERLAPPED overlapped; + uint32 ip_src; + uint32 ip_dest; + uint16 src_port; + uint16 dest_port; + DWORD socket_ttl; +}; + + +int get_socket_index( uint16 src_port, uint16 dest_port, int proto ); +int get_socket_index( uint16 src_port, int proto ); +int get_socket_index( socket_t *cmpl ); +void delete_socket( socket_t *cmpl ); +socket_t *find_socket( uint16 src_port, uint16 dest_port, int proto ); +void add_socket( socket_t *cmpl ); +void close_old_sockets(); +void close_all_sockets(); + + +#endif // _IPSOCKET_H_ diff --git a/BasiliskII/src/Windows/router/mib/interfaces.cpp b/BasiliskII/src/Windows/router/mib/interfaces.cpp new file mode 100755 index 00000000..fad087a8 --- /dev/null +++ b/BasiliskII/src/Windows/router/mib/interfaces.cpp @@ -0,0 +1,68 @@ +/* + * interfaces.cpp - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "interfaces.h" +#include "../dump.h" +#include "mibaccess.h" + +#if DEBUG +#pragma optimize("",off) +#endif + +#include "debug.h" + +static UINT ip_array[100]; +static UINT ip_array_sz = 0; + +void init_interfaces() +{ + MibII _mibs(false); + + ip_array_sz = sizeof(ip_array) / sizeof(ip_array[0]); + + if(_mibs.Init()) { + _mibs.GetIPAddress( ip_array, ip_array_sz ); + } + + if(ip_array_sz == 0) { + ip_array_sz = 1; + ip_array[0] = 0; // localhost + } + + D(bug("init_interfaces() found %d interfaces.\r\n", ip_array_sz)); +} + +void final_interfaces() +{ + // Currently nothing to do. +} + +int get_ip_count() +{ + return ip_array_sz; +} + +uint32 get_ip_by_index( int index ) +{ + return index >= 0 && index < (int)ip_array_sz ? ip_array[index] : 0; +} diff --git a/BasiliskII/src/Windows/router/mib/interfaces.h b/BasiliskII/src/Windows/router/mib/interfaces.h new file mode 100755 index 00000000..ccc94eea --- /dev/null +++ b/BasiliskII/src/Windows/router/mib/interfaces.h @@ -0,0 +1,35 @@ +/* + * intercafes.h - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _INTERFACES_H_ +#define _INTERFACES_H_ + +// A wrapper to the MibII class. Enumerates all ip interfaces +// currently in this computer. The interface list is not static. + +void init_interfaces(); +void final_interfaces(); + +int get_ip_count(); +uint32 get_ip_by_index( int index ); + +#endif // _INTERFACES_H_ diff --git a/BasiliskII/src/Windows/router/mib/mibaccess.cpp b/BasiliskII/src/Windows/router/mib/mibaccess.cpp new file mode 100755 index 00000000..6e555405 --- /dev/null +++ b/BasiliskII/src/Windows/router/mib/mibaccess.cpp @@ -0,0 +1,308 @@ +/* + * MibAccess.cpp + * + * The original code by Stas Khirman modified by Lauri Pesonen, December, 2000: + * + * SnmpUtilVarBindFree(), SnmpUtilOidNCmp() and SnmpUtilOidCpy() now loaded from + * "snmpapi.dll" dynamically instead of linking statically. + * + * MibII ctor now takes a parameter whether to load Winsock or not. + * WSAStartup maintains an internal reference counter so it would have been ok + * to let it load always. + * + * Fixed a bug where the return value of LoadLibrary() was compared against + * HINSTANCE_ERROR instead of NULL. + * + * Removed some type conversion warnings by casting. + * + * Added a check in MibExtLoad ctor that the function entry points were found. + * + * Added a check in GetIPMask() and GetIPAddress() that the library was loaded + * before accessing the functions. + * + * Changed the return type of GetIPAddress() and GetIPMask() from BOOL to void + * as they always returned TRUE. + * + */ + +/************************************************************************/ +/* Copyright (C) Stas Khirman 1998. All rights reserved. */ +/* Written by Stas Khirman (staskh@rocketmail.com). */ +/* and */ +/* Raz Galili (razgalili@hotmail.com) */ +/* */ +/* Free software: no warranty; use anywhere is ok; spread the */ +/* sources; note any modifications; share variations and */ +/* derivatives (including sending to staskh@rocketmail.com). */ +/* */ +/************************************************************************/ + +/* + * MibAccess.cpp - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "sysdeps.h" +#include "mibaccess.h" +#include "..\dynsockets.h" +#include "..\dump.h" + +#if DEBUG +#pragma optimize("",off) +#endif + +#include "debug.h" + +MibExtLoad::MibExtLoad( LPSTR MibDllName, LPSTR SnmpDllName ) +{ + + m_Init = NULL; + + m_InitEx = NULL; + m_Query = NULL; + m_Trap = NULL; + + m_hInst_snmputil = NULL; + + m_SnmpUtilVarBindFree = NULL; + m_SnmpUtilOidNCmp = NULL; + m_SnmpUtilOidCpy = NULL; + + m_hInst = LoadLibrary( MibDllName ); + if(!m_hInst) { + D(bug("MIB: library %s could not be loaded.\r\n", MibDllName)); + return; + } + D(bug("MIB: library %s loaded ok.\r\n", MibDllName)); + + m_Init = (pSnmpExtensionInit)GetProcAddress(m_hInst ,"SnmpExtensionInit"); + m_InitEx= (pSnmpExtensionInitEx)GetProcAddress(m_hInst ,"SnmpExtensionInitEx"); + m_Query = (pSnmpExtensionQuery)GetProcAddress(m_hInst ,"SnmpExtensionQuery"); + m_Trap = (pSnmpExtensionTrap)GetProcAddress(m_hInst ,"SnmpExtensionTrap"); + + if( !m_Init || !m_InitEx || !m_Query || !m_Trap ) + { + D(bug("MIB: required entry points not found in library %s.\r\n", MibDllName)); + FreeLibrary( m_hInst ); + m_hInst = NULL; + } + + m_hInst_snmputil = LoadLibrary( SnmpDllName ); + if(!m_hInst_snmputil){ + D(bug("MIB: library %s could not be loaded.\r\n", SnmpDllName)); + FreeLibrary( m_hInst ); + m_hInst = NULL; + return; + } + D(bug("MIB: library %s loaded ok.\r\n", SnmpDllName)); + + m_SnmpUtilVarBindFree = (VOID (SNMP_FUNC_TYPE *)(SnmpVarBind *))GetProcAddress( m_hInst_snmputil, "SnmpUtilVarBindFree" ); + m_SnmpUtilOidNCmp = (SNMPAPI (SNMP_FUNC_TYPE *)(AsnObjectIdentifier *, AsnObjectIdentifier *, UINT))GetProcAddress( m_hInst_snmputil, "SnmpUtilOidNCmp" ); + m_SnmpUtilOidCpy = (SNMPAPI (SNMP_FUNC_TYPE *)(AsnObjectIdentifier *, AsnObjectIdentifier *))GetProcAddress( m_hInst_snmputil, "SnmpUtilOidCpy" ); + + if( !m_SnmpUtilVarBindFree || !m_SnmpUtilOidNCmp || !m_SnmpUtilOidCpy ) + { + D(bug("MIB: required entry points not found in library %s.\r\n", SnmpDllName)); + FreeLibrary( m_hInst ); + FreeLibrary( m_hInst_snmputil ); + m_hInst = NULL; + m_hInst_snmputil = NULL; + } + + #undef SNMP_FreeVarBind + #undef SNMP_oidncmp + #undef SNMP_oidcpy + + #define SNMP_FreeVarBind m_SnmpUtilVarBindFree + #define SNMP_oidncmp m_SnmpUtilOidNCmp + #define SNMP_oidcpy m_SnmpUtilOidCpy +} + +MibExtLoad::~MibExtLoad() +{ + if( m_hInst ) { + FreeLibrary( m_hInst ); + m_hInst = NULL; + } + if( m_hInst_snmputil ) { + FreeLibrary( m_hInst_snmputil ); + m_hInst_snmputil = NULL; + } +} + +BOOL MibExtLoad::Init(DWORD dwTimeZeroReference,HANDLE *hPollForTrapEvent,AsnObjectIdentifier *supportedView) +{ + if(m_hInst && m_Init) + return m_Init(dwTimeZeroReference,hPollForTrapEvent,supportedView); + return FALSE; +} +BOOL MibExtLoad::InitEx(AsnObjectIdentifier *supportedView) +{ + if(m_hInst && m_InitEx) + return m_InitEx(supportedView); + + return FALSE; +} + +BOOL MibExtLoad::Query(BYTE requestType,OUT RFC1157VarBindList *variableBindings, + AsnInteger *errorStatus,AsnInteger *errorIndex) +{ + if(m_hInst && m_Query) + return m_Query(requestType,variableBindings,errorStatus,errorIndex); + + return FALSE; +} + +BOOL MibExtLoad::Trap(AsnObjectIdentifier *enterprise, AsnInteger *genericTrap, + AsnInteger *specificTrap, AsnTimeticks *timeStamp, + RFC1157VarBindList *variableBindings) +{ + if(m_hInst && m_Trap) + return m_Trap(enterprise, genericTrap,specificTrap, timeStamp, variableBindings); + + return FALSE; +} + +MibII::MibII( bool load_winsock ):MibExtLoad("inetmib1.dll","snmpapi.dll") +{ + WSADATA wsa; + m_load_winsock = load_winsock; + if(load_winsock) { + int err = _WSAStartup( 0x0101, &wsa ); + } +} + +MibII::~MibII() +{ + if(m_load_winsock) _WSACleanup(); +} + +BOOL MibII::Init() +{ + HANDLE PollForTrapEvent; + AsnObjectIdentifier SupportedView; + + return MibExtLoad::Init(GetTickCount(),&PollForTrapEvent,&SupportedView); + +} + + +void MibII::GetIPAddress( UINT IpArray[], UINT &IpArraySize ) +{ + if(!m_hInst) { + IpArraySize = 0; + return; + } + + UINT OID_ipAdEntAddr[] = { 1, 3, 6, 1, 2, 1, 4 , 20, 1 ,1 }; + AsnObjectIdentifier MIB_ipAdEntAddr = { sizeof(OID_ipAdEntAddr)/sizeof(UINT), OID_ipAdEntAddr }; + RFC1157VarBindList varBindList; + RFC1157VarBind varBind[1]; + AsnInteger errorStatus; + AsnInteger errorIndex; + AsnObjectIdentifier MIB_NULL = {0,0}; + BOOL Exit; + int ret; + int IpCount=0; + DWORD dtmp; + + varBindList.list = varBind; + varBindList.len = 1; + varBind[0].name = MIB_NULL; + SNMP_oidcpy(&varBind[0].name,&MIB_ipAdEntAddr); + Exit = FALSE; + + IpCount=0; + while(!Exit){ + ret = Query(ASN_RFC1157_GETNEXTREQUEST,&varBindList,&errorStatus,&errorIndex); + + if(!ret) + Exit=TRUE; + else{ + ret = SNMP_oidncmp(&varBind[0].name,&MIB_ipAdEntAddr,MIB_ipAdEntAddr.idLength); + if(ret!=0){ + Exit=TRUE; + } + else{ + dtmp = *((DWORD *)varBind[0].value.asnValue.address.stream); + IpArray[IpCount] = dtmp; + IpCount++; + if(IpCount>=(int)IpArraySize) + Exit = TRUE; + } + } + } + + IpArraySize = IpCount; + + SNMP_FreeVarBind(&varBind[0]); +} + +void MibII::GetIPMask( UINT IpArray[], UINT &IpArraySize ) +{ + if(!m_hInst) { + IpArraySize = 0; + return; + } + + UINT OID_ipAdEntMask[] = { 1, 3, 6, 1, 2, 1, 4 , 20, 1 ,3 }; + AsnObjectIdentifier MIB_ipAdEntMask = { sizeof(OID_ipAdEntMask)/sizeof(UINT), OID_ipAdEntMask }; + RFC1157VarBindList varBindList; + RFC1157VarBind varBind[1]; + AsnInteger errorStatus; + AsnInteger errorIndex; + AsnObjectIdentifier MIB_NULL = {0,0}; + BOOL Exit; + int ret; + int IpCount=0; + DWORD dtmp; + + varBindList.list = varBind; + varBindList.len = 1; + varBind[0].name = MIB_NULL; + SNMP_oidcpy(&varBind[0].name,&MIB_ipAdEntMask); + Exit = FALSE; + + IpCount=0; + while(!Exit){ + ret = Query(ASN_RFC1157_GETNEXTREQUEST,&varBindList,&errorStatus,&errorIndex); + + if(!ret) + Exit=TRUE; + else{ + ret = SNMP_oidncmp(&varBind[0].name,&MIB_ipAdEntMask,MIB_ipAdEntMask.idLength); + if(ret!=0){ + Exit=TRUE; + } + else{ + dtmp = *((DWORD *)varBind[0].value.asnValue.address.stream); + IpArray[IpCount] = dtmp; + IpCount++; + if(IpCount>=(int)IpArraySize) + Exit = TRUE; + } + } + } + + IpArraySize = IpCount; + + SNMP_FreeVarBind(&varBind[0]); +} diff --git a/BasiliskII/src/Windows/router/mib/mibaccess.h b/BasiliskII/src/Windows/router/mib/mibaccess.h new file mode 100755 index 00000000..2511bfdd --- /dev/null +++ b/BasiliskII/src/Windows/router/mib/mibaccess.h @@ -0,0 +1,83 @@ +////////////////////////////////////////////////////// +// FILE : MibAccess.h +// +// + +#ifndef _SNMP_ACCESS_H_ +#define _SNMP_ACCESS_H_ + +#include +#ifndef SNMP_FUNC_TYPE +#define SNMP_FUNC_TYPE WINAPI +#endif + +////////////////////////////////////////////////////////////// +// Definition of pointers to the four functions in the Mib Dll +// +typedef BOOL (WINAPI *pSnmpExtensionInit)( + IN DWORD dwTimeZeroReference, + OUT HANDLE *hPollForTrapEvent, + OUT AsnObjectIdentifier *supportedView); + +typedef BOOL (WINAPI *pSnmpExtensionTrap)( + OUT AsnObjectIdentifier *enterprise, + OUT AsnInteger *genericTrap, + OUT AsnInteger *specificTrap, + OUT AsnTimeticks *timeStamp, + OUT RFC1157VarBindList *variableBindings); + +typedef BOOL (WINAPI *pSnmpExtensionQuery)( + IN BYTE requestType, + IN OUT RFC1157VarBindList *variableBindings, + OUT AsnInteger *errorStatus, + OUT AsnInteger *errorIndex); + +typedef BOOL (WINAPI *pSnmpExtensionInitEx)(OUT AsnObjectIdentifier *supportedView); + + +class MibExtLoad +{ +public: + MibExtLoad( LPSTR MibDllName, LPSTR SnmpDllName ); + ~MibExtLoad(); + BOOL Init(DWORD dwTimeZeroReference,HANDLE *hPollForTrapEvent,AsnObjectIdentifier *supportedView); + BOOL InitEx(AsnObjectIdentifier *supportedView); + BOOL Query(BYTE requestType,OUT RFC1157VarBindList *variableBindings, + AsnInteger *errorStatus,AsnInteger *errorIndex); + + BOOL Trap(AsnObjectIdentifier *enterprise, AsnInteger *genericTrap, + AsnInteger *specificTrap, AsnTimeticks *timeStamp, + RFC1157VarBindList *variableBindings); + +public: + HINSTANCE m_hInst; + HINSTANCE m_hInst_snmputil; + +private: + pSnmpExtensionInit m_Init; + pSnmpExtensionInitEx m_InitEx; + pSnmpExtensionQuery m_Query; + pSnmpExtensionTrap m_Trap; + +public: + VOID (SNMP_FUNC_TYPE *m_SnmpUtilVarBindFree) (SnmpVarBind *); + SNMPAPI (SNMP_FUNC_TYPE *m_SnmpUtilOidNCmp) (AsnObjectIdentifier *, AsnObjectIdentifier *, UINT); + SNMPAPI (SNMP_FUNC_TYPE *m_SnmpUtilOidCpy) (AsnObjectIdentifier *, AsnObjectIdentifier *); +}; + + +class MibII: public MibExtLoad +{ +public: + MibII( bool load_winsock ); + ~MibII(); + BOOL Init(); + + void GetIPAddress(UINT IpArray[],UINT &IpArraySize); + void GetIPMask(UINT IpArray[],UINT &IpArraySize); + +protected: + bool m_load_winsock; +}; + +#endif diff --git a/BasiliskII/src/Windows/router/router.cpp b/BasiliskII/src/Windows/router/router.cpp new file mode 100755 index 00000000..bfc5ae82 --- /dev/null +++ b/BasiliskII/src/Windows/router/router.cpp @@ -0,0 +1,204 @@ +/* + * router.cpp - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * This could be implemented by writing three (9x,nt,2k) + * NDIS filter drivers. No thanks. + * But this is not easy either. + */ + +#include "sysdeps.h" + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include "cpu_emulation.h" +#include "prefs.h" +#include "ether_windows.h" +#include "ether.h" +#include "router.h" +#include "router_types.h" +#include "dynsockets.h" +#include "ipsocket.h" +#include "iphelp.h" +#include "arp.h" +#include "icmp.h" +#include "udp.h" +#include "tcp.h" +#include "ftp.h" +#include "mib/interfaces.h" +#include "dump.h" + + +#if DEBUG +#pragma optimize("",off) +#endif + +#include "debug.h" + + +uint16 next_ip_ident_number = 1; +uint32 macos_ip_address = 0; +const uint8 router_mac_addr[6] = { '4', '2', '6', '7', '7', '9' }; +uint32 router_ip_address = 0; +bool raw_sockets_available = false; +bool m_router_enabled = true; + + + +// Protected data. +CRITICAL_SECTION router_section; +bool is_router_shutting_down = false; +static HANDLE r_handle = 0; +static unsigned int rh_tid = 0; + + +static void write_ip4( ip_t *ip, int len ) +{ + if(len < sizeof(ip_t)) { + D(bug("Too small ip packet(%d), dropped\r\n", len)); + } else { + uint8 proto = ip->proto; + + // This is a router, decrement the hop count + if( --ip->ttl == 0 ) { + // Most likely this is some Mac traceroute app + D(bug("ip packet ttl expired, proto=%d.\r\n", proto)); + error_winsock_2_icmp( WSAETTLEXCEEDED, ip, len ); + } else { + switch( proto ) { + case ip_proto_icmp: + write_icmp( (icmp_t *)ip, len ); + break; + case ip_proto_tcp: + write_tcp( (tcp_t *)ip, len ); + break; + case ip_proto_udp: + write_udp( (udp_t *)ip, len ); + break; + default: + D(bug("write_ip4() len=%d, proto=%d\r\n", len, proto)); + break; + } + } + } +} + +bool router_write_packet(uint8 *packet, int len) +{ + bool result = false; + + if( len >= 14 ) { + switch( ntohs( ((mac_t *)packet)->type ) ) { + case mac_type_ip4: + write_ip4( (ip_t *)packet, len ); + result = true; + break; + case mac_type_ip6: + D(bug("write_ip6() len=%d; unsupported.\r\n", len)); + result = true; + break; + case mac_type_arp: + result = write_arp( (arp_t *)packet, len ); + break; + } + } + return result; +} + +bool router_read_packet(uint8 *packet, int len) +{ + bool result = false; + + if( len >= 14 ) { + switch( ntohs( ((mac_t *)packet)->type ) ) { + case mac_type_ip4: + case mac_type_ip6: + case mac_type_arp: + result = true; + break; + } + } + return result; +} + +/* + This has nothing to do with TCP TIME_WAITs or CLOSE_WAITs, + the thread is needed to close down expired udp sockets. + Arguably an ugly hack, but needed since there is no way to + listen to all ports w/o writing another ndis filter driver +*/ +static WINAPI unsigned int router_expire_thread(void *arg) +{ + while(!is_router_shutting_down) { + close_old_sockets(); + Sleep(1000); + } + return 0; +} + +void router_init(void) +{ + InitializeCriticalSection( &router_section ); + + m_router_enabled = PrefsFindBool("routerenabled"); + + if(m_router_enabled && dynsockets_init()) { + char me[128]; + if( _gethostname(me, sizeof(me)) == SOCKET_ERROR ) { + D(bug("gethostname() failed, error = %d\r\n", _WSAGetLastError())); + } else { + struct hostent *hent = _gethostbyname(me); + if( hent == NULL ) { + D(bug("gethostbyname() failed, error = %d\r\n", _WSAGetLastError())); + } else { + struct in_addr *ina = (struct in_addr *) *hent->h_addr_list; + router_ip_address = ntohl(ina->s_addr); + D(bug("router protocol address seems to be %s (used only in icmp error messages)\r\n", _inet_ntoa(*ina))); + } + } + is_router_shutting_down = false; + r_handle = (HANDLE)_beginthreadex( 0, 0, router_expire_thread, 0, 0, &rh_tid ); + init_interfaces(); + init_tcp(); + init_udp(); + init_ftp(); + } else { + m_router_enabled = false; + } +} + +void router_final(void) +{ + final_interfaces(); + stop_icmp_listen(); + close_all_sockets(); + if(r_handle) { + is_router_shutting_down = true; + WaitForSingleObject( r_handle, INFINITE ); + final_tcp(); + final_udp(); + dynsockets_final(); + } + DeleteCriticalSection( &router_section ); +} diff --git a/BasiliskII/src/Windows/router/router.h b/BasiliskII/src/Windows/router/router.h new file mode 100755 index 00000000..0820d75a --- /dev/null +++ b/BasiliskII/src/Windows/router/router.h @@ -0,0 +1,55 @@ +/* + * router.h - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ROUTER_H_ +#define _ROUTER_H_ + +extern bool is_router_shutting_down; +extern CRITICAL_SECTION router_section; + +// Increased by one for each ip packet sent to the emulated enet interface. +extern uint16 next_ip_ident_number; + +// Used by incoming icmp packets and internal icmp messages. Host byte order. +extern uint32 macos_ip_address; + +// The magic constant +extern const uint8 router_mac_addr[6]; + +// Used by internal icmp messages. Host byte order. +extern uint32 router_ip_address; + +// False under NT/Win2k if the user has no admin rights +extern bool raw_sockets_available; + + + +// Interface exposed to ether_windows module. +extern bool m_router_enabled; +void router_init(void); +void router_final(void); + +// Both of these return true if the ethernet module should drop the packet. +bool router_write_packet(uint8 *packet, int len); +bool router_read_packet(uint8 *packet, int len); + +#endif // _ROUTER_H_ diff --git a/BasiliskII/src/Windows/router/router_types.h b/BasiliskII/src/Windows/router/router_types.h new file mode 100755 index 00000000..6a839503 --- /dev/null +++ b/BasiliskII/src/Windows/router/router_types.h @@ -0,0 +1,187 @@ +/* + * router_types.h - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ROUTER_TYPES_H_ +#define _ROUTER_TYPES_H_ + +#pragma pack(1) + + +// --------------------------- MAC --------------------------- +typedef struct { + uint8 dest[6]; + uint8 src[6]; + uint16 type; +} ATTRIBUTE_PACKED mac_t; + +enum { + mac_type_llc_ipx_limit = 0x05DC, // <= mac_type_llc_ipx_limit -->> 802.3 MAC frame + mac_type_ip4 = 0x0800, + mac_type_arp = 0x0806, + mac_type_rarp = 0x8035, + mac_type_ip6 = 0x86DD, + mac_type_loopback = 0x9000 +}; + +// --------------------------- ARP --------------------------- +typedef struct { + mac_t mac; + uint16 htype; + uint16 ptype; + uint8 halen; + uint8 palen; + uint16 opcode; + uint8 srch[6]; // size for ethernet + uint8 srcp[4]; // size for ip + uint8 dsth[6]; // size for ethernet + uint8 dstp[4]; // size for ip +} ATTRIBUTE_PACKED arp_t; + +enum { + arp_request = 1, + arp_reply = 2 +}; +enum { + arp_hwtype_enet = 1 +}; + +// --------------------------- IP4 --------------------------- +typedef struct { + mac_t mac; + uint8 header_len:4; + uint8 version:4; + uint8 tos; + uint16 total_len; + uint16 ident; + uint16 flags_n_frag_offset; // foffset 0..11, flags 12..15 + uint8 ttl; + uint8 proto; + uint16 checksum; + uint32 src; + uint32 dest; + // ip options, size = 4 * header_len - 20 +} ATTRIBUTE_PACKED ip_t; + +// Protocol STD numbers +enum { + ip_proto_icmp = IPPROTO_ICMP, + ip_proto_tcp = IPPROTO_TCP, + ip_proto_udp = IPPROTO_UDP +}; + +// --------------------------- ICMP --------------------------- +typedef struct { + ip_t ip; + uint8 type; + uint8 code; + uint16 checksum; + // data +} ATTRIBUTE_PACKED icmp_t; + +enum { + icmp_Echo_reply = 0, + icmp_Destination_unreachable = 3, + icmp_Source_quench = 4, + icmp_Redirect = 5, + icmp_Echo = 8, + icmp_Router_advertisement = 9, + icmp_Router_solicitation = 10, + icmp_Time_exceeded = 11, + icmp_Parameter_problem = 12, + icmp_Time_Stamp_request = 13, + icmp_Time_Stamp_reply = 14, + icmp_Information_request_obsolete = 15, + icmp_Information_reply_obsolete = 16, + icmp_Address_mask_request = 17, + icmp_Address_mask_reply = 18, + icmp_Traceroute = 30, + icmp_Datagram_conversion_error = 31, + icmp_Mobile_host_redirect = 32, + icmp_IPv6_Where_Are_You = 33, + icmp_IPv6_I_Am_Here = 34, + icmp_Mobile_registration_request = 35, + icmp_Mobile_registration_reply = 36, + icmp_Domain_name_request = 37, + icmp_Domain_name_reply = 38, + icmp_SKIP = 39, + icmp_Photuris = 40 +}; + +// --------------------------- TCP --------------------------- +typedef struct { + ip_t ip; + uint16 src_port; + uint16 dest_port; + uint32 seq; + uint32 ack; + uint8 header_len; // note: some reserved bits + uint8 flags; // note: some reserved bits + uint16 window; + uint16 checksum; + uint16 urgent_ptr; + // options + padding: size = dataoffset*4-20 + // data +} ATTRIBUTE_PACKED tcp_t; + +enum { + tcp_flags_URG = 0x20, // The urgent pointer field is significant in this segment. + tcp_flags_ACK = 0x10, // The acknowledgment field is significant in this segment. + tcp_flags_PSH = 0x08, // Push function. + tcp_flags_RST = 0x04, // Resets the connection. + tcp_flags_SYN = 0x02, // Synchronizes the sequence numbers. + tcp_flags_FIN = 0x01 // No more data from sender. +}; + +enum { + tcp_state_closed, + tcp_state_listen, + tcp_state_syn_sent, + tcp_state_syn_rcvd, + tcp_state_established, + tcp_state_close_wait, + tcp_state_last_ack, + tcp_state_finwait_1, + tcp_state_finwait_2, + tcp_state_closing, + tcp_state_time_wait +}; + +// --------------------------- UDP --------------------------- +typedef struct { + ip_t ip; + uint16 src_port; + uint16 dest_port; + uint16 msg_len; + uint16 checksum; + // data +} ATTRIBUTE_PACKED udp_t; + +typedef struct { + uint16 src_lo, src_hi; + uint16 dest_lo, dest_hi; + uint16 proto; + uint16 msg_len; +} ATTRIBUTE_PACKED pseudo_ip_t; + +#pragma pack() + +#endif // _ROUTER_TYPES_H_ diff --git a/BasiliskII/src/Windows/router/tcp.cpp b/BasiliskII/src/Windows/router/tcp.cpp new file mode 100755 index 00000000..21b6321d --- /dev/null +++ b/BasiliskII/src/Windows/router/tcp.cpp @@ -0,0 +1,1606 @@ +/* + * tcp.cpp - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Features implemented: + * state machine, flow control, sequence numbers, RST/SYN/FIN/ACK/PSH + * + * Features not implemented: + * oob data, urgent pointer, window sliding, some options + * "Half-Nagle" implementation is a bit weird (mac-router interface; winsock has it on by default) + * + * + * All possible tcp state machine transitions: + * + * CLOSED -> LISTEN passive open + * CLOSED -> SYN_SENT active open SYN-> + * + * LISTEN -> SYN_SENT send data SYN-> + * LISTEN -> SYN_RCVD ->SYN SYN+ACK-> + * + * SYN_SENT -> SYN_RCVD ->SYN SYN+ACK-> + * SYN_SENT -> ESTABLISHED ->SYN+ACK ACK-> + * SYN_SENT -> CLOSED close/timeout + * + * SYN_RCVD -> CLOSED timeout RST-> + * SYN_RCVD -> LISTEN ->RST + * SYN_RCVD -> ESTABLISHED ->ACK + * SYN_RCVD -> FINWAIT_1 close FIN-> + * + * ESTABLISHED -> FINWAIT_1 close FIN-> + * ESTABLISHED -> CLOSE_WAIT ->FIN ACK-> + * + * CLOSE_WAIT -> LAST_ACK close FIN-> + * + * LAST_ACK -> CLOSED ->ACK + * + * FINWAIT_1 -> CLOSING ->FIN ACK-> + * FINWAIT_1 -> FINWAIT_2 ->ACK + * FINWAIT_1 -> TIME_WAIT ->FIN+ACK ACK-> + * + * FINWAIT_2 -> TIME_WAIT ->FIN ACK-> + * + * CLOSING -> TIME_WAIT ->ACK + * + * TIME_WAIT -> CLOSED timeout (2*msl) + * + */ + +#include "sysdeps.h" + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include "cpu_emulation.h" +#include "ws2tcpip.h" +#include "ether_windows.h" +#include "ether.h" +#include "prefs.h" +#include "router.h" +#include "router_types.h" +#include "dynsockets.h" +#include "iphelp.h" +#include "tcp.h" +#include "dump.h" +#include "mib\interfaces.h" +#include "ftp.h" + +#if DEBUG +#pragma optimize("",off) +#endif + +#include "debug.h" + +// If you need more, use multiple threads. +#define MAX_SOCKETS MAXIMUM_WAIT_OBJECTS + +// If true, always sends the PSH tcp flag with data. +// Otherwise only when a full buffer was received. +#define PUSH_ALWAYS 0 + +// In milliseconds. A TCP implementation should implement +// this dynamically, adapting the timeout value to match to the +// averaged packet round-trip time. +#define RESEND_TIMEOUT 750 + +// Just time out incoming connections after 5 secs if Mac has no time to reply +// No backlogs. +#define SYN_FLOOD_PROTECTION_TIMEOUT 5000 + +const int MAX_SEGMENT_SIZE = 1460; + +// Shorthands +#define ISSET(f,x) ( ((f) & (x)) != 0 ) +#define ISCLEAR(f,x) ( ((f) & (x)) == 0 ) + +// Local aliases +#define URG tcp_flags_URG +#define ACK tcp_flags_ACK +#define PSH tcp_flags_PSH +#define RST tcp_flags_RST +#define SYN tcp_flags_SYN +#define FIN tcp_flags_FIN + +// Local aliases +#define CLOSED tcp_state_closed +#define LISTEN tcp_state_listen +#define SYN_SENT tcp_state_syn_sent +#define SYN_RCVD tcp_state_syn_rcvd +#define ESTABLISHED tcp_state_established +#define CLOSE_WAIT tcp_state_close_wait +#define LAST_ACK tcp_state_last_ack +#define FINWAIT_1 tcp_state_finwait_1 +#define FINWAIT_2 tcp_state_finwait_2 +#define CLOSING tcp_state_closing +#define TIME_WAIT tcp_state_time_wait + +// For debugging only +static const char *_tcp_state_name[] = { + "CLOSED", + "LISTEN", + "SYN_SENT", + "SYN_RCVD", + "ESTABLISHED", + "CLOSE_WAIT", + "LAST_ACK", + "FINWAIT_1", + "FINWAIT_2", + "CLOSING", + "TIME_WAIT" +}; +#define STATENAME(i) _tcp_state_name[i] + +static CRITICAL_SECTION tcp_section; + +typedef struct { + SOCKET s; + int state; + + uint32 ip_src; // "source" is the mac, dest is the remote host, + uint32 ip_dest; // no matter who opened the connection. + uint16 src_port; // all in host byte order. + uint16 dest_port; + + struct sockaddr_in from; // remote host address, network byte order. + int from_len; + + // note: no true windows sliding, only one buffer. + WSABUF buffers_read[1]; // data from remote host to Mac + DWORD buffer_count_read; + DWORD bytes_received; + DWORD flags_read; + WSAOVERLAPPED overlapped_read; + + WSABUF buffers_write[1]; // data from Mac to remote host + DWORD buffer_count_write; + DWORD bytes_written; + DWORD flags_write; + WSAOVERLAPPED overlapped_write; + + bool remote_closed; // remote will not send any more data + bool accept_more_data_from_mac; // are we ready to accept more data from mac + + uint32 seq_in; // will ack this mac sequence number + uint32 seq_out; // next sequence number to mac (unless a resend is needed) + uint32 mac_ack; // mac has acked this byte count. can be used to determined when to send some more data + + uint32 bytes_to_send; // total send block size + uint32 bytes_remaining_to_send; // unsent byte count + + uint16 mac_window; // mac tcp receive window, slides according to the window principle + uint16 our_window; // not really used + uint16 mac_mss; // maximum segment size that mac reported at SYN handshaking + + // resend info + uint32 last_seq_out; // remember last packet seq number if a resend is needed + uint32 resend_timeout; // currently set t0 0.75 secs but not updated + uint32 stream_to_mac_stalled_until; // tick count indicating resend time + + DWORD time_wait; // do a graceful close after MSL*2 + DWORD msl; + + int child; + + WSAEVENT ev; // used to signal remote-initiated close and host-initiated connect. + + bool in_use; +} tcp_socket_t; + +static tcp_socket_t sockets[MAX_SOCKETS]; + +typedef struct { + SOCKET s; + uint16 port; + uint32 ip; + uint32 iface; + bool once; + int parent; + WSAEVENT ev; +} tcp_listening_socket_t; + +static tcp_listening_socket_t l_sockets[MAX_SOCKETS]; + +static void CALLBACK tcp_read_completion( + DWORD error, + DWORD bytes_read, + LPWSAOVERLAPPED lpOverlapped, + DWORD flags +); + +static void CALLBACK tcp_write_completion( + DWORD error, + DWORD bytes_read, + LPWSAOVERLAPPED lpOverlapped, + DWORD flags +); + +// socket utilities assume that the critical section has already been entered. +static void free_socket( const int t ) +{ + _WSAResetEvent( sockets[t].ev ); + if(sockets[t].s != INVALID_SOCKET) { + _closesocket( sockets[t].s ); + sockets[t].s = INVALID_SOCKET; + } + sockets[t].state = CLOSED; + sockets[t].stream_to_mac_stalled_until = 0; + sockets[t].in_use = false; + sockets[t].time_wait = 0; + + // if there was an attached listening socket (ftp), close it. + int lst = sockets[t].child; + if( lst >= 0 ) { + if(l_sockets[lst].s != INVALID_SOCKET) { + D(bug(" closing listening socket %d\r\n", lst)); + _closesocket( l_sockets[lst].s ); + l_sockets[lst].s = INVALID_SOCKET; + } + l_sockets[lst].port = 0; + l_sockets[lst].parent = -1; + } + sockets[t].child = -1; +} + +static int alloc_socket() +{ + static int last_allocated_socket = -1; + + int i = last_allocated_socket; + for( int j=0; j= MAX_SOCKETS ) i = 0; + if( !sockets[i].in_use ) { + D(bug("<%d> Socket allocated\r\n", i)); + + last_allocated_socket = i; + sockets[i].in_use = true; + + sockets[i].s = INVALID_SOCKET; + sockets[i].state = CLOSED; + sockets[i].remote_closed = false; + + sockets[i].accept_more_data_from_mac = false; + + sockets[i].ip_src = sockets[i].ip_dest = 0; + // sockets[i].src_port = sockets[i].dest_port = 0; + + memset( &sockets[i].overlapped_read, 0, sizeof(sockets[i].overlapped_read) ); + sockets[i].overlapped_read.hEvent = (HANDLE)i; + memset( &sockets[i].overlapped_write, 0, sizeof(sockets[i].overlapped_write) ); + sockets[i].overlapped_write.hEvent = (HANDLE)i; + + sockets[i].bytes_received = 0; + sockets[i].bytes_written = 0; + + sockets[i].flags_read = 0; + sockets[i].flags_write = 0; + + // sockets[i].from_len = sizeof(struct sockaddr_in); + // memset( &sockets[i].from, 0, sizeof(sockets[i].from) ); + // sockets[i].from.sin_family = AF_INET; + + sockets[i].buffer_count_read = 1; + sockets[i].buffers_read[0].len = MAX_SEGMENT_SIZE; + if(!sockets[i].buffers_read[0].buf) { + sockets[i].buffers_read[0].buf = new char [sockets[i].buffers_read[0].len]; + } + + sockets[i].buffer_count_write = 1; + sockets[i].buffers_write[0].len = MAX_SEGMENT_SIZE; + if(!sockets[i].buffers_write[0].buf) { + sockets[i].buffers_write[0].buf = new char [sockets[i].buffers_write[0].len]; + } + + sockets[i].mac_window = MAX_SEGMENT_SIZE; // updated for all mac datagrams + sockets[i].our_window = MAX_SEGMENT_SIZE; // should use about 8-16 kB, really + sockets[i].mac_mss = 0; // not known yet + + sockets[i].time_wait = 0; + sockets[i].msl = 5000L; // The round-trip time can be hard to estimate. + + sockets[i].seq_in = 0; + sockets[i].seq_out = 0x00000001; + sockets[i].mac_ack = 0; + sockets[i].stream_to_mac_stalled_until = 0; + + sockets[i].resend_timeout = RESEND_TIMEOUT; + + sockets[i].child = -1; + + break; + } + } + if(i == MAX_SOCKETS) { + D(bug("Out of free sockets\r\n")); + i = -1; + } + return i; +} + +static int alloc_new_socket( const uint16 src_port, const uint16 dest_port, const uint32 ip_dest ) +{ + int t = alloc_socket(); + + if(t >= 0) { + sockets[t].s = _socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); + if(sockets[t].s == INVALID_SOCKET) { + free_socket( t ); + t = -1; + } else { + sockets[t].src_port = src_port; + sockets[t].dest_port = dest_port; + + sockets[t].from_len = sizeof(sockets[t].from); + memset( &sockets[t].from, 0, sockets[t].from_len ); + sockets[t].from.sin_family = AF_INET; + sockets[t].from.sin_port = htons(dest_port); + sockets[t].from.sin_addr.s_addr = htonl(ip_dest); + + struct sockaddr_in to; + memset( &to, 0, sizeof(to) ); + to.sin_family = AF_INET; + + if( _bind ( sockets[t].s, (const struct sockaddr *)&to, sizeof(to) ) == 0 ) { + D(bug("<%d> socket bound\r\n", t)); + } else { + if( _WSAGetLastError() == WSAEINPROGRESS ) { + D(bug("<%d> bind: a blocking call is in progress.\r\n", t)); + } else { + D(bug("<%d> bind failed with error code %d\r\n", t, _WSAGetLastError())); + } + free_socket( t ); + t = -1; + } + } + } + return t; +} + +static int get_socket_index( const uint16 src_port, const uint16 dest_port ) +{ + for( int i=0; i= 0 ) { + if( sockets[i].s == INVALID_SOCKET ) { + D(bug("find_socket reusing slot %d...\r\n", i)); + sockets[i].in_use = false; + } else { + D(bug("find_socket forcing close %d...\r\n", i)); + free_socket( i ); + } + i = -1; + } + } + + D(bug("<%d> find_socket(%d,%d): %s\r\n", i, src_port, dest_port, i>=0 ? "found" : "not found")); + + return i; +} + +static int alloc_listen_socket( const uint16 port, const uint32 ip, const uint32 iface, const bool once ) +{ + static int last_allocated_socket = -1; + + int i = last_allocated_socket; + + for( int j=0; j= MAX_SOCKETS ) i = 0; + if( l_sockets[i].port == 0 ) { + D(bug("[%d] Slot allocated for listening port %d\r\n", i, port)); + l_sockets[i].port = port; + l_sockets[i].ip = ip; + l_sockets[i].iface = iface; + l_sockets[i].once = once; + l_sockets[i].parent = -1; + last_allocated_socket = i; + _WSAResetEvent( l_sockets[i].ev ); + return i; + } + } + return -1; +} + +static void tcp_start_listen( const int i ) +{ + if( l_sockets[i].port ) { + uint32 iface = l_sockets[i].iface; + + D(bug("[%d] binding to interface 0x%08X\r\n", i, iface)); + + l_sockets[i].s = _socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); + if(l_sockets[i].s != INVALID_SOCKET) { + struct sockaddr_in to; + memset( &to, 0, sizeof(to) ); + to.sin_family = AF_INET; + to.sin_port = htons( l_sockets[i].port ); + to.sin_addr.s_addr = htonl( iface ); + + if( _bind ( l_sockets[i].s, (const struct sockaddr *)&to, sizeof(to) ) == 0 ) + { + D(bug("[%d] socket bound to port %d on interface 0x%08X\r\n", i, l_sockets[i].port, iface)); + if( _listen( l_sockets[i].s, SOMAXCONN ) == SOCKET_ERROR ) { + D(bug("[%d] listen() failed with error code %d\r\n", i, _WSAGetLastError())); + } else { + D(bug("[%d] listening to port %d\r\n", i, l_sockets[i].port)); + _WSAResetEvent( l_sockets[i].ev ); + if( SOCKET_ERROR == _WSAEventSelect( l_sockets[i].s, l_sockets[i].ev, FD_ACCEPT ) ) { + D(bug("[%d] WSAEventSelect() failed with error code %d\r\n", i, _WSAGetLastError())); + } + } + } else { + D(bug("[%d] bind to port %d failed with error code %d\r\n", i, l_sockets[i].port, _WSAGetLastError())); + } + } else { + D(bug("[%d] could not create a socket for port %d, error = %d\r\n", i, l_sockets[i].port, _WSAGetLastError())); + } + } +} + +static void set_ttl( const int t, const uint8 ttl ) +{ + int _ttl = ttl; // defensive programming, I know VCx + + if(_setsockopt( sockets[t].s, IPPROTO_IP, IP_TTL, (const char *)&_ttl, sizeof(int) ) == SOCKET_ERROR ) { + D(bug("<%d> could not set ttl to %d, error=%d\r\n", t, ttl, _WSAGetLastError())); + } else { + D(bug("<%d> ttl set to %d.\r\n", t, ttl)); + } +} + +static void tcp_reply( const int flags, const int t ) +{ + int tcp_size = sizeof(tcp_t); + + tcp_t *tcp = (tcp_t *)malloc( tcp_size ); + if(tcp) { + memcpy( tcp->ip.mac.dest, ether_addr, 6 ); + memcpy( tcp->ip.mac.src, router_mac_addr, 6 ); + tcp->ip.mac.type = htons(mac_type_ip4); + + tcp->ip.version = 4; + tcp->ip.header_len = 5; + tcp->ip.tos = 0; + tcp->ip.total_len = htons(tcp_size - sizeof(mac_t)); + tcp->ip.ident = htons(next_ip_ident_number++); + tcp->ip.flags_n_frag_offset = 0; + tcp->ip.ttl = 128; + tcp->ip.proto = ip_proto_tcp; + tcp->ip.src = htonl(sockets[t].ip_dest); + tcp->ip.dest = htonl(sockets[t].ip_src); + make_ip4_checksum( (ip_t *)tcp ); + + D(bug("<%d> Reply: Seq=%d, Ack=%d\r\n", t, sockets[t].seq_out, sockets[t].seq_in)); + + tcp->src_port = htons(sockets[t].dest_port); + tcp->dest_port = htons(sockets[t].src_port); + tcp->seq = htonl(sockets[t].seq_out); + tcp->ack = htonl(sockets[t].seq_in); + tcp->header_len = (uint8)( 20 << 2 ); + tcp->flags = flags; + tcp->window = htons( sockets[t].our_window ); + tcp->urgent_ptr = 0; + make_tcp_checksum( tcp, tcp_size ); + + // dump_bytes( (uint8 *)tcp, tcp_size ); + + enqueue_packet( (uint8 *)tcp, tcp_size ); + free(tcp); + } +} + +static bool has_mac_read_space( const int t ) +{ + uint32 pending_bytes = sockets[t].seq_out - sockets[t].mac_ack; + uint32 mac_can_accept_bytes = sockets[t].mac_window - pending_bytes; + + D(bug("<%d> mac_can_accept_bytes = %d\r\n", t, mac_can_accept_bytes)); + + // Modified Nagle, effectively disabling window sliding (which I don't support anyway): + return pending_bytes == 0; + + // Use more of window bandwidth + // Enabling this would require that the buffers seq numbers are stored somewhere + // return mac_can_accept_bytes >= sockets[t].buffers_read[0].len; +} + +static bool b_recfrom( const int t ) +{ + bool result; + + if( !has_mac_read_space(t) ) { + D(bug("<%d> read stalled, mac cannot accept any more data\r\n", t)); + + sockets[t].stream_to_mac_stalled_until = GetTickCount() + sockets[t].resend_timeout; + return true; + } + + int ret = _WSARecv( + sockets[t].s, + sockets[t].buffers_read, + sockets[t].buffer_count_read, + &sockets[t].bytes_received, + &sockets[t].flags_read, + &sockets[t].overlapped_read, + tcp_read_completion + ); + + if(ret == SOCKET_ERROR) { + int socket_error = _WSAGetLastError(); + if(socket_error == WSA_IO_PENDING) { + D(bug("<%d> WSARecv() i/o pending\r\n", t)); + result = true; + } else { + D(bug("<%d> WSARecv() returned error %d\r\n", t, socket_error)); + result = false; + } + } else /*if(ret == 0) */ { + D(bug("<%d> WSARecv() ok\r\n", t)); + // Completion routine call is already scheduled. + result = true; + } + return result; +} + +static bool b_send( const int t ) +{ + int ret = _WSASend( + sockets[t].s, + sockets[t].buffers_write, + sockets[t].buffer_count_write, + &sockets[t].bytes_written, + sockets[t].flags_write, + &sockets[t].overlapped_write, + tcp_write_completion + ); + + bool result; + if(ret == SOCKET_ERROR) { + int socket_error = _WSAGetLastError(); + if(socket_error == WSA_IO_PENDING) { + D(bug("<%d> WSASend() i/o pending\r\n", t)); + result = true; + } else { + D(bug("<%d> WSASend() returned %d\r\n", t, socket_error)); + result = false; + } + } else /*if(ret == 0) */ { + D(bug("<%d> WSASend() ok\r\n", t)); + // Completion routine call is already scheduled. + result = true; + } + return result; +} + +static void send_buffer( const int t, const bool resending ) +{ + if(resending) { + if(sockets[t].last_seq_out == 0) { + D(bug("<%d> resend failure\r\n", t )); + return; + } + sockets[t].seq_out = sockets[t].last_seq_out; + } else { + sockets[t].last_seq_out = sockets[t].seq_out; + } + + D(bug("<%d> %s data to Mac: Seq=%d, Ack=%d\r\n", t, (resending ? "resending" : "sending"), sockets[t].seq_out, sockets[t].seq_in)); + + uint32 bytes_read = sockets[t].bytes_received; + + if( sockets[t].mac_mss && bytes_read > sockets[t].mac_mss ) { + D(bug("<%d> impossible: %d bytes to send, Mac mss is only %d\r\n", t, sockets[t].mac_mss && bytes_read, sockets[t].mac_mss)); + } + + int tcp_size = sizeof(tcp_t) + bytes_read; + + tcp_t *tcp = (tcp_t *)malloc( tcp_size ); + if(tcp) { + // Build MAC + // memcpy( tcp->ip.mac.dest, sockets[t].mac_src, 6 ); + memcpy( tcp->ip.mac.dest, ether_addr, 6 ); + memcpy( tcp->ip.mac.src, router_mac_addr, 6 ); + tcp->ip.mac.type = htons(mac_type_ip4); + + // Build IP + tcp->ip.version = 4; + tcp->ip.header_len = 5; + tcp->ip.tos = 0; + tcp->ip.total_len = htons(sizeof(tcp_t) - sizeof(mac_t) + bytes_read); // no options + tcp->ip.ident = htons(next_ip_ident_number++); + tcp->ip.flags_n_frag_offset = 0; + tcp->ip.ttl = 128; // one hop actually! + tcp->ip.proto = ip_proto_tcp; + tcp->ip.src = htonl(sockets[t].ip_dest); + tcp->ip.dest = htonl(sockets[t].ip_src); + make_ip4_checksum( (ip_t *)tcp ); + + // Copy payload (used by tcp checksum) + memcpy( (char *)tcp + sizeof(tcp_t), sockets[t].buffers_read[0].buf, bytes_read ); + + // Build tcp + tcp->src_port = htons(sockets[t].dest_port); + tcp->dest_port = htons(sockets[t].src_port); + + tcp->seq = htonl(sockets[t].seq_out); + tcp->ack = htonl(sockets[t].seq_in); + + tcp->header_len = (uint8)( 20 << 2 ); +#if PUSH_ALWAYS + tcp->flags = ACK|PSH; +#else + tcp->flags = (bytes_read == MAX_SEGMENT_SIZE) ? ACK : (ACK|PSH); +#endif + tcp->window = htons( sockets[t].our_window ); + tcp->urgent_ptr = 0; + make_tcp_checksum( tcp, tcp_size ); + + sockets[t].seq_out += bytes_read; + + // dump_bytes( (uint8 *)tcp, tcp_size ); + + enqueue_packet( (uint8 *)tcp, tcp_size ); + free(tcp); + } +} + +static void CALLBACK tcp_read_completion( + DWORD error, + DWORD bytes_read, + LPWSAOVERLAPPED lpOverlapped, + DWORD flags +) +{ + EnterCriticalSection( &tcp_section ); + + const int t = (int)lpOverlapped->hEvent; + + sockets[t].bytes_received = bytes_read; + + D(bug("<%d> tcp_read_completion(error=%d, bytes_read=%d)\r\n", t, error, bytes_read)); + + D(bug("<%d> tcp_read_completion() start, old state = %s\r\n", t, STATENAME(sockets[t].state))); + + if(!sockets[t].in_use) { + D(bug("<%d> ignoring canceled read\r\n", t)); + } else { + if( error != 0 ) { + D(bug("<%d> resetting after read error\r\n", t)); + tcp_reply( RST, t ); + free_socket(t); + } else { + if(bytes_read == 0) { + _closesocket( sockets[t].s ); + sockets[t].s = INVALID_SOCKET; + } else if( bytes_read > 0) { + send_buffer( t, false ); + } + + switch( sockets[t].state ) { + case SYN_RCVD: + if( bytes_read == 0 ) { + D(bug("<%d> Closing: SYN_RCVD -> FINWAIT_1\r\n", t)); + tcp_reply( ACK|FIN, t ); + sockets[t].seq_out++; + sockets[t].state = FINWAIT_1; + } + break; + case ESTABLISHED: + if( bytes_read == 0 ) { + D(bug("<%d> Closing: ESTABLISHED -> FINWAIT_1\r\n", t)); + tcp_reply( ACK|FIN, t ); + sockets[t].seq_out++; + sockets[t].state = FINWAIT_1; + } + break; + case LISTEN: + tcp_reply( SYN, t ); + sockets[t].seq_out++; + sockets[t].state = SYN_SENT; + sockets[t].time_wait = GetTickCount() + SYN_FLOOD_PROTECTION_TIMEOUT; + D(bug("<%d> LISTEN -> SYN_SENT\r\n", t)); + break; + case CLOSE_WAIT: + if( bytes_read == 0) { + tcp_reply( ACK|FIN, t ); + sockets[t].seq_out++; + sockets[t].state = LAST_ACK; + D(bug("<%d> Closing: CLOSE_WAIT -> LAST_ACK\r\n", t)); + if(sockets[t].remote_closed) { + // Just in case that mac gets out of sync. + _closesocket(sockets[t].s); + sockets[t].s = INVALID_SOCKET; + } + } + break; + default: + break; + } + + if(!is_router_shutting_down && sockets[t].s != INVALID_SOCKET) { + if(sockets[t].state != LISTEN) { + b_recfrom(t); + } + } + } + } + + LeaveCriticalSection( &tcp_section ); +} + +static void CALLBACK tcp_write_completion( + DWORD error, + DWORD bytes_written, + LPWSAOVERLAPPED lpOverlapped, + DWORD flags +) +{ + EnterCriticalSection( &tcp_section ); + + const int t = (int)lpOverlapped->hEvent; + + sockets[t].bytes_written = bytes_written; + sockets[t].bytes_remaining_to_send -= bytes_written; + + D(bug("<%d> tcp_write_completion(error=%d, bytes_written=%d)\r\n", t, error, bytes_written)); + + if(!sockets[t].in_use) { + D(bug("<%d> ignoring canceled write\r\n", t)); + } else { + if(is_router_shutting_down || sockets[t].s == INVALID_SOCKET) { + D(bug("<%d> is not alive for sending.\r\n", t)); + } else { + if( sockets[t].bytes_remaining_to_send <= 0 ) { + D(bug("<%d> all data sent, accepting some more.\r\n", t)); + sockets[t].seq_in += sockets[t].bytes_to_send; + sockets[t].bytes_to_send = sockets[t].bytes_remaining_to_send = 0; // superfluous + tcp_reply( ACK, t ); + sockets[t].accept_more_data_from_mac = true; + } else { + D(bug("<%d> %d bytes (of %d total) remaining, sending.\r\n", t, sockets[t].bytes_remaining_to_send, sockets[t].bytes_to_send)); + sockets[t].buffers_write[0].len = sockets[t].bytes_remaining_to_send; + char *p = sockets[t].buffers_write[0].buf; + memmove( p, &p[bytes_written], sockets[t].bytes_remaining_to_send ); + if(!b_send(t)) { + } else { + } + } + } + } + + LeaveCriticalSection( &tcp_section ); +} + +static void tcp_connect_callback( const int t ) +{ + D(bug("<%d> tcp_connect_callback() start, old state = %s\r\n", t, STATENAME(sockets[t].state))); + + switch( sockets[t].state ) { + case LISTEN: + tcp_reply( SYN|ACK, t ); + sockets[t].seq_out++; + sockets[t].state = SYN_RCVD; + D(bug("<%d> Connect: LISTEN -> SYN_RCVD\r\n", t)); + break; + default: + break; + } + D(bug("<%d> tcp_connect_callback() end, new state = %s\r\n", t, STATENAME(sockets[t].state))); +} + +static void tcp_accept_callback( const int lst ) +{ + D(bug("[%d] tcp_accept_callback()\r\n", lst)); + + struct sockaddr_in to; + memset( &to, 0, sizeof(to) ); + to.sin_family = AF_INET; + int tolen = sizeof(to); + + SOCKET s = _accept( l_sockets[lst].s, (struct sockaddr *)&to, &tolen ); + if( s == INVALID_SOCKET ) { + D(bug("[%d] connection not accepted, error code %d\r\n", lst, _WSAGetLastError())); + } else { + _WSAEventSelect( s, 0, 0 ); + + uint16 src_port = l_sockets[lst].port; + uint16 dest_port = ntohs(to.sin_port); + uint32 ip_dest = ntohl(to.sin_addr.s_addr); + + D(bug("[%d] connection accepted, local port:%d, remote %s:%d\r\n", lst, src_port, _inet_ntoa(to.sin_addr), dest_port)); + + if( l_sockets[lst].ip != 0 && l_sockets[lst].ip != ip_dest ) { + _closesocket( s ); + D(bug("[%d] authorization failure. connection closed.\r\n", lst )); + } else { + int t = alloc_new_socket( src_port, dest_port, ip_dest ); + if( t < 0 ) { + D(bug("<%d> out of slot space, connection dropped\r\n", t )); + free_socket(t); + } else { + sockets[t].s = s; + sockets[t].state = LISTEN; + sockets[t].src_port = src_port; + sockets[t].dest_port = dest_port; + sockets[t].ip_src = macos_ip_address; + sockets[t].ip_dest = ip_dest; + + sockets[t].seq_out = 0x00000001; + sockets[t].seq_in = 0; // not known yet + sockets[t].mac_ack = sockets[t].seq_out; // zero out pending bytes + + tcp_reply( SYN, t ); + sockets[t].seq_out++; + sockets[t].state = SYN_SENT; + sockets[t].time_wait = GetTickCount() + SYN_FLOOD_PROTECTION_TIMEOUT; + D(bug("<%d> Connect: LISTEN -> SYN_SENT\r\n", t)); + + _WSAResetEvent( sockets[t].ev ); + if( SOCKET_ERROR == _WSAEventSelect( sockets[t].s, sockets[t].ev, FD_CLOSE ) ) { + D(bug("<%d> WSAEventSelect() failed with error code %d\r\n", t, _WSAGetLastError())); + } + + // No data from the remote host is needed until the connection is established. + // So don't initiate read yet. + } + } + } +} + +/* + MSS is the only option I care about, and since I'm on ethernet + I already pretty much know everything needed. + + AFAIK window scaling is not in effect unless both parties specify it, + and I'm not doing it. +*/ +static void process_options( const int t, const uint8 *opt, int len, uint32 &mss ) +{ + mss = 0; + + while( len > 0 ) { + switch( *opt ) { + case 0: // End of Option List + D(bug("<%d> End of Option List\r\n", t)); + len = 0; + break; + case 1: // No-Operation + D(bug("<%d> No-Operation\r\n", t)); + len--; + opt++; + break; + case 2: // Maximum Segment Size + { + mss = ntohs( *((uint16 *)&opt[2]) ); + D(bug("<%d> Maximum Segment Size = %d\r\n", t, mss)); + len -= 4; + opt += 4; + } + break; + case 3: // Window Scale + { + int wscale = opt[2]; + D(bug("<%d> Window Scale = %d\r\n", t, (int)wscale)); + len -= 3; + opt += 3; + } + break; + case 4: // Sack-Permitted + D(bug("<%d> Sack-Permitted option is set\r\n", t)); + len -= 2; + opt += 2; + break; + case 5: // Sack + { + int sack_len = opt[1]; + int hf = (sack_len-2) / 4; + D(bug("<%d> Sack, %d half-blocks\r\n", t, hf)); + len -= sack_len; + opt += sack_len; + } + break; + case 8: // Time Stamps + { + int valve = ntohl( *((uint32 *)&opt[2]) ); + int ereply = ntohl( *((uint32 *)&opt[6]) ); + D(bug("<%d> Time Stamps, TS valve = 0x%X, TS echo reply = 0x%X\r\n", t, valve, ereply)); + len -= 10; + opt += 10; + } + break; + default: + D(bug("<%d> Unknown tcp header option 0x%02x, breaking out\r\n", t, (int)*opt)); + len = 0; + break; + } + } +} + +void write_tcp( tcp_t *tcp, int len ) +{ + if(len < sizeof(tcp_t)) { + D(bug("<%d> Too small tcp packet(%d) on unknown slot, dropped\r\n", -1, len)); + return; + } + uint16 src_port = ntohs(tcp->src_port); + uint16 dest_port = ntohs(tcp->dest_port); + + BOOL ok = true; + BOOL handle_data = false; + BOOL initiate_read = false; + + EnterCriticalSection( &tcp_section ); + + int t = find_socket( src_port, dest_port ); + + if(t < 0) { + t = alloc_new_socket( src_port, dest_port, ntohl(tcp->ip.dest) ); + ok = t >= 0; + } + + if(ok) { + D(bug("<%d> write_tcp %d bytes from port %d to port %d\r\n", t, len, src_port, dest_port)); + } else { + D(bug("<%d> FAILED write_tcp %d bytes from port %d to port %d\r\n", t, len, src_port, dest_port)); + } + + if( ok && ISSET(tcp->flags,RST) ) { + D(bug("<%d> RST set, resetting socket\r\n", t)); + if( sockets[t].s != INVALID_SOCKET ) { + D(bug("<%d> doing an extra shutdown (ie4)\r\n", t)); + _shutdown( sockets[t].s, SD_BOTH ); + } + free_socket( t ); + ok = false; + } + + if(ok) { + D(bug("<%d> State machine start = %s\r\n", t, STATENAME(sockets[t].state))); + + // always update receive window + sockets[t].mac_window = ntohs(tcp->window); + + int header_len = tcp->header_len >> 2; + int option_bytes = header_len - 20; + char *data = (char *)tcp + sizeof(tcp_t) + option_bytes; + int dlen = len - sizeof(tcp_t) - option_bytes; + + if( !ISSET(tcp->flags,ACK) ) { + D(bug("<%d> ACK not set\r\n", t)); + } + if( ISSET(tcp->flags,SYN) ) { + D(bug("<%d> SYN set\r\n", t)); + + // Note that some options are valid even if there is no SYN. + // I don't care about those however. + + uint32 new_mss; + process_options( t, (uint8 *)data - option_bytes, option_bytes, new_mss ); + if(new_mss) { + sockets[t].mac_mss = (int)new_mss; + if( new_mss < sockets[t].buffers_read[0].len ) { + sockets[t].buffers_read[0].len = new_mss; + } + D(bug("<%d> Max segment size set to %d\r\n", t, new_mss)); + } + } + if( ISSET(tcp->flags,FIN) ) { + D(bug("<%d> FIN set\r\n", t)); + } + + // The sequence number Mac expects to see next time. + sockets[t].mac_ack = ntohl(tcp->ack); + + D(bug("<%d> From Mac: Seq=%d, Ack=%d, window=%d, router Seq=%d\r\n", t, ntohl(tcp->seq), sockets[t].mac_ack, sockets[t].mac_window, sockets[t].seq_out)); + + if( sockets[t].stream_to_mac_stalled_until && + sockets[t].mac_ack == sockets[t].seq_out && + (sockets[t].state == ESTABLISHED || sockets[t].state == CLOSE_WAIT) ) + { + if( has_mac_read_space(t) ) { + initiate_read = true; + sockets[t].stream_to_mac_stalled_until = 0; + D(bug("<%d> read resumed, mac can accept more data\r\n", t)); + } + } + + switch( sockets[t].state ) { + case CLOSED: + sockets[t].src_port = src_port; + sockets[t].dest_port = dest_port; + sockets[t].ip_src = ntohl(tcp->ip.src); + sockets[t].ip_dest = ntohl(tcp->ip.dest); + + if( ISSET(tcp->flags,SYN) ) { + + sockets[t].seq_out = 0x00000001; + sockets[t].seq_in = ntohl(tcp->seq) + 1; + + _WSAResetEvent( sockets[t].ev ); + if( SOCKET_ERROR == _WSAEventSelect( sockets[t].s, sockets[t].ev, FD_CONNECT | FD_CLOSE ) ) { + D(bug("<%d> WSAEventSelect() failed with error code %d\r\n", t, _WSAGetLastError())); + } + + D(bug("<%d> connecting local port %d to remote %s:%d\r\n", t, src_port, _inet_ntoa(sockets[t].from.sin_addr), dest_port)); + + sockets[t].state = LISTEN; + if( _WSAConnect( + sockets[t].s, + (const struct sockaddr *)&sockets[t].from, + sockets[t].from_len, + NULL, NULL, + NULL, NULL + ) == SOCKET_ERROR ) + { + int connect_error = _WSAGetLastError(); + if( connect_error == WSAEWOULDBLOCK ) { + D(bug("<%d> WSAConnect() i/o pending.\r\n", t)); + } else { + D(bug("<%d> WSAConnect() failed with error %d.\r\n", t, connect_error)); + } + } else { + D(bug("<%d> WSAConnect() ok.\r\n", t)); + } + } else { + if( ISSET(tcp->flags,FIN) ) { + D(bug("<%d> No SYN but FIN on a closed socket.\r\n", t)); + free_socket(t); + } else { + D(bug("<%d> No SYN on a closed socket. resetting.\r\n", t)); + free_socket(t); + } + } + break; + case LISTEN: + // handled in connect callback + break; + case SYN_SENT: + if( ISSET(tcp->flags,SYN) && ISSET(tcp->flags,ACK) ) { + sockets[t].seq_in = ntohl(tcp->seq) + 1; + tcp_reply( ACK, t ); + sockets[t].state = ESTABLISHED; + initiate_read = true; + sockets[t].accept_more_data_from_mac = true; + sockets[t].time_wait = 0; + } else if( ISSET(tcp->flags,SYN) ) { + sockets[t].seq_in = ntohl(tcp->seq) + 1; + tcp_reply( ACK|SYN, t ); + sockets[t].seq_out++; + sockets[t].state = SYN_RCVD; + sockets[t].time_wait = 0; + } else if( ISSET(tcp->flags,ACK) ) { + // What was the bright idea here. + D(bug("<%d> State is SYN_SENT, but got only ACK from Mac??\r\n", t)); + sockets[t].state = FINWAIT_2; + sockets[t].time_wait = 0; + } + break; + case SYN_RCVD: + if( ISSET(tcp->flags,ACK) ) { + sockets[t].state = ESTABLISHED; + handle_data = true; + initiate_read = true; + sockets[t].accept_more_data_from_mac = true; + } + break; + case ESTABLISHED: + if( ISSET(tcp->flags,FIN) ) { + sockets[t].seq_in++; + tcp_reply( ACK, t ); + _shutdown( sockets[t].s, SD_SEND ); + sockets[t].state = CLOSE_WAIT; + } + handle_data = true; + break; + case CLOSE_WAIT: + // handled in tcp_read_completion + break; + case LAST_ACK: + if( ISSET(tcp->flags,ACK) ) { + D(bug("<%d> LAST_ACK received, socket closed\r\n", t)); + free_socket( t ); + } + break; + case FINWAIT_1: + if( ISSET(tcp->flags,FIN) && ISSET(tcp->flags,ACK) ) { + sockets[t].seq_in++; + tcp_reply( ACK, t ); + if(sockets[t].remote_closed) { + _closesocket(sockets[t].s); + sockets[t].s = INVALID_SOCKET; + } else { + _shutdown( sockets[t].s, SD_SEND ); + } + sockets[t].state = TIME_WAIT; + sockets[t].time_wait = GetTickCount() + 2 * sockets[t].msl; + } else if( ISSET(tcp->flags,FIN) ) { + sockets[t].seq_in++; + tcp_reply( ACK, t ); + if(sockets[t].remote_closed) { + _closesocket(sockets[t].s); + sockets[t].s = INVALID_SOCKET; + } else { + _shutdown( sockets[t].s, SD_SEND ); + } + sockets[t].state = CLOSING; + } else if( ISSET(tcp->flags,ACK) ) { + sockets[t].state = FINWAIT_2; + } + break; + case FINWAIT_2: + if( ISSET(tcp->flags,FIN) ) { + sockets[t].seq_in++; + tcp_reply( ACK, t ); + if(sockets[t].remote_closed) { + _closesocket(sockets[t].s); + sockets[t].s = INVALID_SOCKET; + } else { + _shutdown( sockets[t].s, SD_SEND ); + } + sockets[t].state = TIME_WAIT; + sockets[t].time_wait = GetTickCount() + 2 * sockets[t].msl; + } + break; + case CLOSING: + if( ISSET(tcp->flags,ACK) ) { + sockets[t].state = TIME_WAIT; + sockets[t].time_wait = GetTickCount() + 2 * sockets[t].msl; + } + break; + case TIME_WAIT: + // Catching stray packets: wait MSL * 2 seconds, -> CLOSED + // Timer already set since we might not get here at all. + // I'm using exceptionally low MSL value (5 secs). + D(bug("<%d> time wait, datagram discarded\r\n", t)); + break; + } + + // The "t" descriptor may already be freed. However, it's safe + // to peek the state value inside the critical section. + D(bug("<%d> State machine end = %s\r\n", t, STATENAME(sockets[t].state))); + + D(bug("<%d> handle_data=%d, initiate_read=%d\r\n", t, handle_data, initiate_read)); + + if( handle_data && dlen && sockets[t].accept_more_data_from_mac ) { + if( sockets[t].seq_in != ntohl(tcp->seq) ) { + D(bug("<%d> dropping duplicate datagram seq=%d, expected=%d\r\n", t, ntohl(tcp->seq), sockets[t].seq_in)); + } else { + set_ttl( t, tcp->ip.ttl ); + + struct sockaddr_in to; + memset( &to, 0, sizeof(to) ); + to.sin_family = AF_INET; + to.sin_port = tcp->dest_port; + to.sin_addr.s_addr = tcp->ip.dest; + + D(bug("<%d> sending %d bytes to remote host\r\n", t, dlen)); + + sockets[t].accept_more_data_from_mac = false; + + if( dlen > MAX_SEGMENT_SIZE ) { + D(bug("<%d> IMPOSSIBLE: b_send() dropped %d bytes! \r\n", t, dlen-MAX_SEGMENT_SIZE)); + dlen = MAX_SEGMENT_SIZE; + } + + memcpy( sockets[t].buffers_write[0].buf, data, dlen ); + + sockets[t].buffers_write[0].len = dlen; + sockets[t].bytes_remaining_to_send = dlen; + sockets[t].bytes_to_send = dlen; + + bool send_now = false; + if( ISSET(tcp->flags,PSH) ) { + send_now = true; + } else { + // todo -- delayed send + send_now = true; + } + + if(send_now) { + + // Patch ftp server or client address if needed. + + int lst = 1; + bool is_pasv; + uint16 ftp_data_port = 0; + + if(ftp_is_ftp_port(sockets[t].src_port)) { + // Local ftp server may be entering to passive mode. + is_pasv = true; + ftp_parse_port_command( + sockets[t].buffers_write[0].buf, + dlen, + ftp_data_port, + is_pasv + ); + } else if(ftp_is_ftp_port(sockets[t].dest_port)) { + // Local ftp client may be using port command. + is_pasv = false; + ftp_parse_port_command( + sockets[t].buffers_write[0].buf, + dlen, + ftp_data_port, + is_pasv + ); + } + + if(ftp_data_port) { + D(bug("<%d> ftp %s command detected, port %d\r\n", t, (is_pasv ? "SERVER PASV REPLY" : "CLIENT PORT"), ftp_data_port )); + + // Note: for security reasons, only allow incoming connection from sockets[t].ip_dest + lst = alloc_listen_socket( ftp_data_port, sockets[t].ip_dest, 0/*iface*/, true ); + + if(lst < 0) { + D(bug("<%d> no more free slots\r\n", t)); + } else { + // First start listening (need to know the local name later) + tcp_start_listen( lst ); + + // When t is closed, lst must be closed too. + sockets[t].child = lst; + l_sockets[lst].parent = t; + + // Find out the local name + struct sockaddr_in name; + int namelen = sizeof(name); + memset( &name, 0, sizeof(name) ); + if( _getsockname( sockets[t].s, (struct sockaddr *)&name, &namelen ) == SOCKET_ERROR ) { + D(bug("_getsockname() failed, error=%d\r\n", _WSAGetLastError() )); + } + + ftp_modify_port_command( + sockets[t].buffers_write[0].buf, + dlen, + MAX_SEGMENT_SIZE, + ntohl(name.sin_addr.s_addr), + ftp_data_port, + is_pasv + ); + + sockets[t].buffers_write[0].len = dlen; + sockets[t].bytes_remaining_to_send = dlen; + // Do not change "bytes_to_send" field as it is used for ack calculation + } + } // end of ftp patch + + if(!b_send(t)) { + // on error, close the ftp data listening socket if one was created + if(lst >= 0) { + D(bug("[%d] closing listening port %d after write error\r\n", t, l_sockets[lst].port)); + _closesocket( l_sockets[lst].s ); + l_sockets[lst].s = INVALID_SOCKET; + l_sockets[lst].port = 0; + l_sockets[lst].ip = 0; + l_sockets[lst].parent = -1; + sockets[t].child = -1; + } + } + } + } + } + + if(initiate_read) { + if(!b_recfrom(t)) { + // post icmp error message + } + } + } + + LeaveCriticalSection( &tcp_section ); +} + +/* + - Dispatch remote close and connect events. + - Expire time-waits. + - Handle resend timeouts. +*/ +static WINAPI unsigned int tcp_connect_close_thread(void *arg) +{ + WSAEVENT wait_handles[MAX_SOCKETS]; + + for( int i=0; i= WAIT_OBJECT_0 && ret < WAIT_OBJECT_0 + MAX_SOCKETS ) { + const int t = ret - WAIT_OBJECT_0; + + D(bug("<%d> Event %d\r\n", t, ret)); + + if(sockets[t].in_use) { + WSANETWORKEVENTS what; + + if( _WSAEnumNetworkEvents( sockets[t].s, sockets[t].ev, &what ) != SOCKET_ERROR ) { + if( what.lNetworkEvents & FD_CONNECT ) { + if( what.iErrorCode[FD_CONNECT_BIT] == 0 ) { + D(bug("<%d> Connect ok\r\n", t)); + tcp_connect_callback(t); + } else { + D(bug("<%d> Connect error=%d\r\n", t, what.iErrorCode[FD_CONNECT_BIT])); + // Post icmp error + } + } else if( what.lNetworkEvents & FD_CLOSE ) { + if( what.iErrorCode[FD_CLOSE_BIT] == 0 ) { + D(bug("<%d> graceful close, state = %s\r\n", t, STATENAME(sockets[t].state))); + } else { + D(bug("<%d> abortive close, state = %s, code=%d\r\n", t, STATENAME(sockets[t].state), what.iErrorCode[FD_CLOSE_BIT])); + } + sockets[t].remote_closed = true; + } + } else { + int err = _WSAGetLastError(); + if( err == WSAENOTSOCK ) { + D(bug("<%d> WSAEnumNetworkEvents: socket is already closed\r\n", t)); + } else { + D(bug("<%d> WSAEnumNetworkEvents failed with error code %d, freeing slot\r\n", t, err)); + free_socket( t ); + } + } + } + _WSAResetEvent( sockets[t].ev ); + } else { + static int interval = 5; + if( !--interval ) { + for( int i=0; i= tmw ) { + if( sockets[i].state == SYN_SENT ) { + /* + A very basic SYN flood protection. Note that watching + SYN_SENT instead of SYN_RCVD, because the state codes are + from the point of view of the Mac-Router interface, not Router-Remote. + */ + D(bug("<%d> SYN_SENT time-out expired\r\n", i)); + } else { + D(bug("<%d> TIME_WAIT expired\r\n", i)); + } + free_socket( i ); + } + } else if( stl ) { + if( sockets[i].state == ESTABLISHED ) { + if( GetTickCount() >= stl ) { + D(bug("<%d> RESEND timeout expired\r\n", i)); + sockets[i].stream_to_mac_stalled_until = GetTickCount() + sockets[i].resend_timeout; + send_buffer( i, true ); + } + } else { + sockets[i].stream_to_mac_stalled_until = 0; + } + } + } + } + interval = 5; + } + } + LeaveCriticalSection( &tcp_section ); + } + return 0; +} + +static WINAPI unsigned int tcp_listen_thread(void *arg) +{ + WSAEVENT wait_handles[MAX_SOCKETS]; + + for( int i=0; i= WAIT_OBJECT_0 && ret < WAIT_OBJECT_0 + MAX_SOCKETS ) { + const int lst = ret - WAIT_OBJECT_0; + + D(bug("[%d] connection attempt to port %d\r\n", lst, l_sockets[lst].port)); + + WSANETWORKEVENTS what; + + if( _WSAEnumNetworkEvents( l_sockets[lst].s, l_sockets[lst].ev, &what ) != SOCKET_ERROR ) { + if( what.lNetworkEvents & FD_ACCEPT ) { + if( what.iErrorCode[FD_ACCEPT_BIT] == 0 ) { + D(bug("[%d] Connect ok\r\n", lst)); + tcp_accept_callback(lst); + } else { + D(bug("[%d] Connect error=%d\r\n", lst, what.iErrorCode[FD_ACCEPT_BIT])); + // Post icmp error + } + } + } + + // close on errors too + if(l_sockets[lst].once) { + D(bug("[%d] once mode: closing listening socket on port %d\r\n", lst, l_sockets[lst].port)); + if( _closesocket( l_sockets[lst].s ) == SOCKET_ERROR ) { + int err = _WSAGetLastError(); + D(bug("[%d] close error %d\r\n", lst, err)); + } + + l_sockets[lst].s = INVALID_SOCKET; + l_sockets[lst].port = 0; + l_sockets[lst].ip = 0; + + int t = l_sockets[lst].parent; + if( t >= 0 ) { + sockets[t].child = -1; + } + l_sockets[lst].parent = -1; + } + + _WSAResetEvent( l_sockets[lst].ev ); + } + LeaveCriticalSection( &tcp_section ); + } + return 0; +} + +/* + tcp_port= [,] + tcp_port=21,192.168.0.1 +*/ + +static void init_tcp_listen_ports() +{ + int32 index = 0; + const char *port_str; + while ((port_str = PrefsFindString("tcp_port", index++)) != NULL) { + uint32 iface = 0; + char *if_str = strchr(port_str,','); + if(if_str) { + *if_str++ = 0; + uint32 if_net = _inet_addr( if_str ); + if(if_net == INADDR_NONE) if_net = INADDR_ANY; + iface = ntohl( if_net ); + } + uint16 port = (uint16)strtoul( port_str, 0, 0 ); + if( port ) { + uint32 ip = 0; + bool once = false; + alloc_listen_socket( port, ip, iface, once ); + } + } +} + +static HANDLE tcp_handle = 0; +static HANDLE tcp_l_handle = 0; + +void init_tcp() +{ + InitializeCriticalSection( &tcp_section ); + + for( int i=0; ihEvent; + + // It's not easy to know whether empty upd datagrams should be passed along. doh. + if(error == 0 && bytes_read > 0) { + + if(bytes_read > 1460) { + D(bug("discarding oversized udp packet, size = \r\n", bytes_read)); + } else { + struct sockaddr_in name; + int namelen = sizeof(name); + memset( &name, 0, sizeof(name) ); + if( _getsockname( cmpl->s, (struct sockaddr *)&name, &namelen ) == SOCKET_ERROR ) { + D(bug("_getsockname() failed, error=%d\r\n", _WSAGetLastError() )); + } else { + D(bug("_getsockname(): port=%d\r\n", ntohs(name.sin_port) )); + } + + int udp_size = sizeof(udp_t) + bytes_read; + udp_t *udp = (udp_t *)malloc( udp_size ); + if(udp) { + mac_t *mac = (mac_t *)udp; + ip_t *ip = (ip_t *)udp; + + // Build MAC + // memcpy( udp->ip.mac.dest, cmpl->mac_src, 6 ); + memcpy( mac->dest, ether_addr, 6 ); + memcpy( mac->src, router_mac_addr, 6 ); + mac->type = htons(mac_type_ip4); + + // Build IP + ip->version = 4; + ip->header_len = 5; + ip->tos = 0; + ip->total_len = htons(sizeof(udp_t) - sizeof(mac_t) + bytes_read); // no options + ip->ident = htons(next_ip_ident_number++); // htons() might be a macro... but does not really matter here. + ip->flags_n_frag_offset = 0; + ip->ttl = 128; // one hop actually! + ip->proto = ip_proto_udp; + ip->src = htonl(cmpl->ip_dest); + ip->dest = htonl(cmpl->ip_src); + make_ip4_checksum( (ip_t *)udp ); + + // Copy payload (used by UDP checksum) + memcpy( (char *)udp + sizeof(udp_t), cmpl->buffers[0].buf, bytes_read ); + + // Build UDP + udp->src_port = htons(cmpl->dest_port); + udp->dest_port = htons(cmpl->src_port); + udp->msg_len = htons(sizeof(udp_t) - sizeof(ip_t) + bytes_read); // no options + make_udp_checksum( udp ); + + dump_bytes( (uint8 *)udp, udp_size ); + + enqueue_packet( (uint8 *)udp, udp_size ); + free(udp); + } + } + } + + if(!is_router_shutting_down && cmpl->s != INVALID_SOCKET && cmpl->b_recfrom()) { + cmpl->socket_ttl = GetTickCount() + 60000L; + } else { + delete_socket( cmpl ); + } +} + +void write_udp( udp_t *udp, int len ) +{ + if( len < sizeof(udp_t) ) { + D(bug("Too small udp packet(%d), dropped\r\n", len)); + return; + } + + uint16 src_port = ntohs(udp->src_port); + uint16 dest_port = ntohs(udp->dest_port); + + BOOL ok = true; + + socket_t *cmpl = find_socket( src_port, dest_port, IPPROTO_UDP ); + + BOOL old_socket_found = cmpl != 0; + + if(!cmpl) { + cmpl = new socket_t(IPPROTO_UDP); + if(cmpl) { + cmpl->s = _socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if(cmpl->s == INVALID_SOCKET) { + delete cmpl; + cmpl = 0; + ok = false; + } else { + cmpl->src_port = src_port; + cmpl->dest_port = dest_port; + add_socket( cmpl ); + } + } else { + ok = false; + } + } + + if(ok) { + cmpl->src_port = src_port; + cmpl->dest_port = dest_port; + cmpl->ip_src = ntohl(udp->ip.src); + cmpl->ip_dest = ntohl(udp->ip.dest); + + struct sockaddr_in to; + memset( &to, 0, sizeof(to) ); + to.sin_family = AF_INET; + to.sin_port = udp->dest_port; + to.sin_addr.s_addr = udp->ip.dest; + + char *data = (char *)udp + sizeof(udp_t); + int dlen = len - sizeof(udp_t); + + // ttl changed, update checksum + make_udp_checksum( udp ); + + cmpl->set_ttl( udp->ip.ttl ); + + bool please_close = true; + /* + Note that broadcast messages fill fail, no setsockopt(SO_BROADCAST). + That's exactly what I want. + */ + if(SOCKET_ERROR != _sendto( cmpl->s, data, dlen, 0, (struct sockaddr *)&to, sizeof(to) )) { + if(old_socket_found) { + // This socket is not overlapped. + please_close = false; + } else { + if(cmpl->b_recfrom()) please_close = false; + } + cmpl->socket_ttl = GetTickCount() + 60000L; + } else { + int socket_error = _WSAGetLastError(); + D(bug("_sendto() completed with error %d\r\n", socket_error)); + // TODO: check this out: error_winsock_2_icmp() uses router_ip_address + // as source ip; but it's probably allright + error_winsock_2_icmp( socket_error, (ip_t *)udp, len ); + } + if(please_close) { + delete_socket(cmpl); + } + } +} + +void init_udp() +{ +} + +void final_udp() +{ +} diff --git a/BasiliskII/src/Windows/router/udp.h b/BasiliskII/src/Windows/router/udp.h new file mode 100755 index 00000000..75481340 --- /dev/null +++ b/BasiliskII/src/Windows/router/udp.h @@ -0,0 +1,38 @@ +/* + * udp.h - ip router + * + * Basilisk II (C) 1997-2001 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _UDP_H_ +#define _UDP_H_ + +void write_udp( udp_t *udp, int len ); + +void CALLBACK udp_read_completion( + DWORD error, + DWORD bytes_read, + LPWSAOVERLAPPED lpOverlapped, + DWORD flags +); + +void init_udp(); +void final_udp(); + +#endif // _UDP_H_