From 6a5ea92a4eb6d8fb7855051d9471f8412a1fd2aa Mon Sep 17 00:00:00 2001 From: Andrea Date: Sun, 8 May 2022 17:26:01 +0200 Subject: [PATCH] Uthernet II: add virtual DNS feature (PR #1097) Uthernet II: add extended feature to virtualise DNS requests. . This allows pure TCP/UDP sockets to run *without* MACRAW requests (and so without libpcap). . Raw sockets will not work. . Add configuration for Virtual DNS. libpcap: ensure all functions check if the library is loaded before using it. Uthernet 1: do NOT overwrite tfe_cannot_use as it should only reflect the availability of npcap on *this* system. Add Copyright notice, and mention Virtual DNS in html. --- AppleWinExpress2019.vcxproj | 3 + AppleWinExpress2019.vcxproj.filters | 9 + help/uthernet.html | 3 + resource/Applewin.rc | 9 +- resource/resource.h | 2 + source/Common.h | 1 + source/Configuration/Config.cpp | 98 +++++++++++ source/Configuration/Config.h | 69 +------- source/Configuration/PageAdvanced.cpp | 1 + source/Configuration/PageConfig.cpp | 8 +- source/Configuration/PageConfigTfe.cpp | 99 +++++------ source/Configuration/PageConfigTfe.h | 4 +- source/Configuration/PageDisk.cpp | 2 + source/Configuration/PropertySheetHelper.cpp | 8 +- source/FrameBase.h | 2 +- source/SaveState.cpp | 1 + source/Tfe/DNS.cpp | 49 ++++++ source/Tfe/DNS.h | 4 + source/Tfe/NetworkBackend.h | 3 + source/Tfe/PCapBackend.cpp | 44 +++-- source/Tfe/PCapBackend.h | 17 +- source/Tfe/tfearch.cpp | 93 ++++++---- source/Tfe/tfearch.h | 6 +- source/Uthernet1.cpp | 16 +- source/Uthernet2.cpp | 168 ++++++++++++++++--- source/Uthernet2.h | 13 ++ source/Utilities.cpp | 16 +- source/W5100.h | 9 + source/Windows/AppleWin.cpp | 1 + source/Windows/Win32Frame.cpp | 4 +- source/Windows/Win32Frame.h | 2 +- source/Windows/WinFrame.cpp | 1 + 32 files changed, 539 insertions(+), 226 deletions(-) create mode 100644 source/Configuration/Config.cpp create mode 100644 source/Tfe/DNS.cpp create mode 100644 source/Tfe/DNS.h diff --git a/AppleWinExpress2019.vcxproj b/AppleWinExpress2019.vcxproj index 573a9e0b..94c69529 100644 --- a/AppleWinExpress2019.vcxproj +++ b/AppleWinExpress2019.vcxproj @@ -114,6 +114,7 @@ + @@ -157,6 +158,7 @@ + @@ -223,6 +225,7 @@ + diff --git a/AppleWinExpress2019.vcxproj.filters b/AppleWinExpress2019.vcxproj.filters index c3ce4b15..d6419dda 100644 --- a/AppleWinExpress2019.vcxproj.filters +++ b/AppleWinExpress2019.vcxproj.filters @@ -262,6 +262,12 @@ Source Files\Uthernet + + Source Files\Uthernet + + + Source Files\Configuration + @@ -600,6 +606,9 @@ Source Files\Uthernet + + Source Files\Uthernet + diff --git a/help/uthernet.html b/help/uthernet.html index ca1373a9..8591da6c 100644 --- a/help/uthernet.html +++ b/help/uthernet.html @@ -97,5 +97,8 @@
  • after loading a save-state file, TCP and UDP sockets are closed
  • +

    The card implements a Virtual DNS + interface (not found on real hardware) for Apple II software to run without raw sockets: + this allows operation on any type of network.

    diff --git a/resource/Applewin.rc b/resource/Applewin.rc index f1e34e4b..a6be7002 100644 --- a/resource/Applewin.rc +++ b/resource/Applewin.rc @@ -206,19 +206,22 @@ BEGIN PUSHBUTTON "&Browse...",IDC_CIDERPRESS_BROWSE,161,202,50,14 END -IDD_TFE_SETTINGS_DIALOG DIALOGEX 0, 0, 270, 100 +IDD_TFE_SETTINGS_DIALOG DIALOGEX 0, 0, 271, 155 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Ethernet Settings" FONT 8, "MS Shell Dlg", 0, 0, 0x0 BEGIN LTEXT "Ethernet",IDC_TFE_SETTINGS_ENABLE_T,9,7,30,8 COMBOBOX IDC_TFE_SETTINGS_ENABLE,45,5,60,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Virtual DNS",IDC_CHECK_TFE_VIRTUAL_DNS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,7,50,8 LTEXT "Interface",IDC_TFE_SETTINGS_INTERFACE_T,9,24,30,8 COMBOBOX IDC_TFE_SETTINGS_INTERFACE,45,22,210,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "",IDC_TFE_SETTINGS_INTERFACE_NAME,9,44,250,8 LTEXT "",IDC_TFE_SETTINGS_INTERFACE_DESC,9,60,250,8 - DEFPUSHBUTTON "Ok",IDOK,20,75,50,14 - PUSHBUTTON "Cancel",IDCANCEL,80,75,50,14 + GROUPBOX "Npcap",IDC_STATIC,5,75,260,55 + LTEXT "",IDC_TFE_NPCAP_INFO,12,86,240,36 + DEFPUSHBUTTON "Ok",IDOK,20,135,50,14 + PUSHBUTTON "Cancel",IDCANCEL,80,135,50,14 END IDD_PROPPAGE_ADVANCED DIALOGEX 0, 0, 210, 217 diff --git a/resource/resource.h b/resource/resource.h index 819b1011..74eefc3a 100644 --- a/resource/resource.h +++ b/resource/resource.h @@ -119,6 +119,8 @@ #define IDC_FOURPLAY_CONFIG 1087 #define IDC_SNESMAX_CONFIG 1088 #define IDC_CHECK_VIDHD_IN_SLOT3 1089 +#define IDC_CHECK_TFE_VIRTUAL_DNS 1090 +#define IDC_TFE_NPCAP_INFO 1091 #define IDM_EXIT 40001 #define IDM_HELP 40002 #define IDM_ABOUT 40003 diff --git a/source/Common.h b/source/Common.h index 8df91b2a..98256d9e 100644 --- a/source/Common.h +++ b/source/Common.h @@ -108,6 +108,7 @@ enum AppMode_e #define REGVALUE_WINDOW_SCALE "Window Scale" #define REGVALUE_UTHERNET_ACTIVE "Uthernet Active" // GH#977: Deprecated from 1.30.5 #define REGVALUE_UTHERNET_INTERFACE "Uthernet Interface" +#define REGVALUE_UTHERNET_VIRTUAL_DNS "Uthernet Virtual DNS" #define REGVALUE_SLOT4 "Slot 4" // GH#977: Deprecated from 1.30.4 #define REGVALUE_SLOT5 "Slot 5" // GH#977: Deprecated from 1.30.4 #define REGVALUE_VERSION "Version" diff --git a/source/Configuration/Config.cpp b/source/Configuration/Config.cpp new file mode 100644 index 00000000..be6aa0a8 --- /dev/null +++ b/source/Configuration/Config.cpp @@ -0,0 +1,98 @@ +/* + AppleWin : An Apple //e emulator for Windows + + Copyright (C) 1994-1996, Michael O'Brien + Copyright (C) 1999-2001, Oliver Schmidt + Copyright (C) 2002-2005, Tom Charlesworth + Copyright (C) 2006-2007, Tom Charlesworth, Michael Pohoreski + + AppleWin 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. + + AppleWin 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 AppleWin; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "StdAfx.h" +#include "Config.h" +#include "../CardManager.h" +#include "../Interface.h" // VideoRefreshRate_e, GetVideoRefreshRate() +#include "../Uthernet2.h" +#include "../Tfe/PCapBackend.h" + +// zero initialise +CConfigNeedingRestart::CConfigNeedingRestart() +{ + m_Apple2Type = A2TYPE_APPLE2; + m_CpuType = CPU_UNKNOWN; + memset(m_Slot, 0, sizeof(m_Slot)); + m_SlotAux = CT_Empty; + m_tfeVirtualDNS = false; + m_bEnableTheFreezesF8Rom = 0; + m_uSaveLoadStateMsg = 0; + m_videoRefreshRate = VR_NONE; +} + +// create from current global configuration +CConfigNeedingRestart CConfigNeedingRestart::Create() +{ + CConfigNeedingRestart config; + config.Reload(); + return config; +} + +// update from current global configuration +void CConfigNeedingRestart::Reload() +{ + m_Apple2Type = GetApple2Type(); + m_CpuType = GetMainCpu(); + CardManager& cardManager = GetCardMgr(); + for (UINT slot = SLOT0; slot < NUM_SLOTS; slot++) + m_Slot[slot] = cardManager.QuerySlot(slot); + m_SlotAux = cardManager.QueryAux(); + m_tfeInterface = PCapBackend::GetRegistryInterface(SLOT3); + m_tfeVirtualDNS = Uthernet2::GetRegistryVirtualDNS(SLOT3); + m_bEnableTheFreezesF8Rom = GetPropertySheet().GetTheFreezesF8Rom(); + m_uSaveLoadStateMsg = 0; + m_videoRefreshRate = GetVideo().GetVideoRefreshRate(); +} + +const CConfigNeedingRestart& CConfigNeedingRestart::operator= (const CConfigNeedingRestart& other) +{ + m_Apple2Type = other.m_Apple2Type; + m_CpuType = other.m_CpuType; + memcpy(m_Slot, other.m_Slot, sizeof(m_Slot)); + m_SlotAux = other.m_SlotAux; + m_tfeInterface = other.m_tfeInterface; + m_tfeVirtualDNS = other.m_tfeVirtualDNS; + m_bEnableTheFreezesF8Rom = other.m_bEnableTheFreezesF8Rom; + m_uSaveLoadStateMsg = other.m_uSaveLoadStateMsg; + m_videoRefreshRate = other.m_videoRefreshRate; + return *this; +} + +bool CConfigNeedingRestart::operator== (const CConfigNeedingRestart& other) const +{ + return m_Apple2Type == other.m_Apple2Type && + m_CpuType == other.m_CpuType && + memcmp(m_Slot, other.m_Slot, sizeof(m_Slot)) == 0 && + m_SlotAux == other.m_SlotAux && + m_tfeInterface == other.m_tfeInterface && + m_tfeVirtualDNS == other.m_tfeVirtualDNS && + m_bEnableTheFreezesF8Rom == other.m_bEnableTheFreezesF8Rom && + m_uSaveLoadStateMsg == other.m_uSaveLoadStateMsg && + m_videoRefreshRate == other.m_videoRefreshRate; +} + +bool CConfigNeedingRestart::operator!= (const CConfigNeedingRestart& other) const +{ + return !operator==(other); +} diff --git a/source/Configuration/Config.h b/source/Configuration/Config.h index aae3c5ba..1f4947c0 100644 --- a/source/Configuration/Config.h +++ b/source/Configuration/Config.h @@ -1,86 +1,33 @@ #pragma once #include "../Core.h" -#include "../CardManager.h" #include "../CPU.h" -#include "../DiskImage.h" // Disk_Status_e -#include "../Harddisk.h" -#include "../Interface.h" // VideoRefreshRate_e, GetVideoRefreshRate() -#include "../Tfe/PCapBackend.h" +#include "../Video.h" class CConfigNeedingRestart { public: // zero initialise - CConfigNeedingRestart() - { - m_Apple2Type = A2TYPE_APPLE2; - m_CpuType = CPU_UNKNOWN; - memset(m_Slot, 0, sizeof(m_Slot)); - m_SlotAux = CT_Empty; - m_bEnableTheFreezesF8Rom = 0; - m_uSaveLoadStateMsg = 0; - m_videoRefreshRate = VR_NONE; - } + CConfigNeedingRestart(); // create from current global configuration - static CConfigNeedingRestart Create() - { - CConfigNeedingRestart config; - config.Reload(); - return config; - } + static CConfigNeedingRestart Create(); // update from current global configuration - void Reload() - { - m_Apple2Type = GetApple2Type(); - m_CpuType = GetMainCpu(); - CardManager& cardManager = GetCardMgr(); - for (UINT slot = SLOT0; slot < NUM_SLOTS; slot++) - m_Slot[slot] = cardManager.QuerySlot(slot); - m_SlotAux = cardManager.QueryAux(); - m_tfeInterface = PCapBackend::tfe_interface; - m_bEnableTheFreezesF8Rom = GetPropertySheet().GetTheFreezesF8Rom(); - m_uSaveLoadStateMsg = 0; - m_videoRefreshRate = GetVideo().GetVideoRefreshRate(); - } + void Reload(); - const CConfigNeedingRestart& operator= (const CConfigNeedingRestart& other) - { - m_Apple2Type = other.m_Apple2Type; - m_CpuType = other.m_CpuType; - memcpy(m_Slot, other.m_Slot, sizeof(m_Slot)); - m_SlotAux = other.m_SlotAux; - m_tfeInterface = other.m_tfeInterface; - m_bEnableTheFreezesF8Rom = other.m_bEnableTheFreezesF8Rom; - m_uSaveLoadStateMsg = other.m_uSaveLoadStateMsg; - m_videoRefreshRate = other.m_videoRefreshRate; - return *this; - } + const CConfigNeedingRestart& operator= (const CConfigNeedingRestart& other); - bool operator== (const CConfigNeedingRestart& other) const - { - return m_Apple2Type == other.m_Apple2Type && - m_CpuType == other.m_CpuType && - memcmp(m_Slot, other.m_Slot, sizeof(m_Slot)) == 0 && - m_SlotAux == other.m_SlotAux && - m_tfeInterface == other.m_tfeInterface && - m_bEnableTheFreezesF8Rom == other.m_bEnableTheFreezesF8Rom && - m_uSaveLoadStateMsg == other.m_uSaveLoadStateMsg && - m_videoRefreshRate == other.m_videoRefreshRate; - } + bool operator== (const CConfigNeedingRestart& other) const; - bool operator!= (const CConfigNeedingRestart& other) const - { - return !operator==(other); - } + bool operator!= (const CConfigNeedingRestart& other) const; eApple2Type m_Apple2Type; eCpuType m_CpuType; SS_CARDTYPE m_Slot[NUM_SLOTS]; SS_CARDTYPE m_SlotAux; std::string m_tfeInterface; + bool m_tfeVirtualDNS; UINT m_bEnableTheFreezesF8Rom; UINT m_uSaveLoadStateMsg; VideoRefreshRate_e m_videoRefreshRate; diff --git a/source/Configuration/PageAdvanced.cpp b/source/Configuration/PageAdvanced.cpp index 1fcd5c84..ea98579d 100644 --- a/source/Configuration/PageAdvanced.cpp +++ b/source/Configuration/PageAdvanced.cpp @@ -30,6 +30,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../ParallelPrinter.h" #include "../Registry.h" #include "../SaveState.h" +#include "../CardManager.h" #include "../resource/resource.h" CPageAdvanced* CPageAdvanced::ms_this = 0; // reinit'd in ctor diff --git a/source/Configuration/PageConfig.cpp b/source/Configuration/PageConfig.cpp index 98fe811a..e5add898 100644 --- a/source/Configuration/PageConfig.cpp +++ b/source/Configuration/PageConfig.cpp @@ -30,6 +30,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../Windows/Win32Frame.h" #include "../Registry.h" #include "../SerialComms.h" +#include "../CardManager.h" +#include "../Uthernet2.h" +#include "../Tfe/PCapBackend.h" +#include "../Interface.h" #include "../resource/resource.h" CPageConfig* CPageConfig::ms_this = 0; // reinit'd in ctor @@ -114,6 +118,7 @@ INT_PTR CPageConfig::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPA ui_tfe_settings_dialog(hWnd); m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3] = m_PageConfigTfe.m_tfe_selected; m_PropertySheetHelper.GetConfigNew().m_tfeInterface = m_PageConfigTfe.m_tfe_interface_name; + m_PropertySheetHelper.GetConfigNew().m_tfeVirtualDNS = m_PageConfigTfe.m_tfe_virtual_dns; InitOptions(hWnd); break; @@ -261,7 +266,8 @@ INT_PTR CPageConfig::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPA break; } - m_PageConfigTfe.m_tfe_interface_name = PCapBackend::tfe_interface; + m_PageConfigTfe.m_tfe_interface_name = PCapBackend::GetRegistryInterface(SLOT3); + m_PageConfigTfe.m_tfe_virtual_dns = Uthernet2::GetRegistryVirtualDNS(SLOT3); } InitOptions(hWnd); diff --git a/source/Configuration/PageConfigTfe.cpp b/source/Configuration/PageConfigTfe.cpp index a130f180..344db91b 100644 --- a/source/Configuration/PageConfigTfe.cpp +++ b/source/Configuration/PageConfigTfe.cpp @@ -135,37 +135,16 @@ BOOL CPageConfigTfe::get_tfename(int number, std::string & name, std::string & d return FALSE; } -int CPageConfigTfe::gray_ungray_items(HWND hwnd) +void CPageConfigTfe::gray_ungray_items(HWND hwnd) { - int enable; - int number; - - int disabled = 0; - PCapBackend::get_disabled_state(&disabled); - - if (disabled) - { - EnableWindow(GetDlgItem(hwnd, IDC_TFE_SETTINGS_ENABLE_T), 0); - EnableWindow(GetDlgItem(hwnd, IDC_TFE_SETTINGS_ENABLE), 0); - EnableWindow(GetDlgItem(hwnd, IDOK), 0); - SetWindowText(GetDlgItem(hwnd,IDC_TFE_SETTINGS_INTERFACE_NAME), ""); - SetWindowText(GetDlgItem(hwnd,IDC_TFE_SETTINGS_INTERFACE_DESC), ""); - enable = 0; - } - else - { - enable = SendMessage(GetDlgItem(hwnd, IDC_TFE_SETTINGS_ENABLE), CB_GETCURSEL, 0, 0) ? 1 : 0; - } - - EnableWindow(GetDlgItem(hwnd, IDC_TFE_SETTINGS_INTERFACE_T), enable); - EnableWindow(GetDlgItem(hwnd, IDC_TFE_SETTINGS_INTERFACE), enable); + const int enable = SendMessage(GetDlgItem(hwnd, IDC_TFE_SETTINGS_ENABLE), CB_GETCURSEL, 0, 0); if (enable) { std::string name; std::string description; - number = SendMessage(GetDlgItem(hwnd, IDC_TFE_SETTINGS_INTERFACE), CB_GETCURSEL, 0, 0); + const int number = SendMessage(GetDlgItem(hwnd, IDC_TFE_SETTINGS_INTERFACE), CB_GETCURSEL, 0, 0); if (get_tfename(number, name, description)) { @@ -179,7 +158,7 @@ int CPageConfigTfe::gray_ungray_items(HWND hwnd) SetWindowText(GetDlgItem(hwnd, IDC_TFE_SETTINGS_INTERFACE_DESC), ""); } - return disabled ? 1 : 0; + EnableWindow(GetDlgItem(hwnd, IDC_CHECK_TFE_VIRTUAL_DNS), enable == 2); } void CPageConfigTfe::init_tfe_dialog(HWND hwnd) @@ -193,6 +172,23 @@ void CPageConfigTfe::init_tfe_dialog(HWND hwnd) uilib_adjust_group_width(hwnd, ms_leftgroup); uilib_move_group(hwnd, ms_rightgroup, xsize + 30); + if (PCapBackend::tfe_is_npcap_loaded()) + { + const char * version = PCapBackend::tfe_lib_version(); + SetWindowText(GetDlgItem(hwnd, IDC_TFE_NPCAP_INFO), version); + } + else + { + EnableWindow(GetDlgItem(hwnd, IDC_TFE_SETTINGS_INTERFACE), 0); + EnableWindow(GetDlgItem(hwnd, IDC_TFE_SETTINGS_INTERFACE_NAME), 0); + EnableWindow(GetDlgItem(hwnd, IDC_TFE_SETTINGS_INTERFACE_DESC), 0); + + SetWindowText(GetDlgItem(hwnd, IDC_TFE_NPCAP_INFO), + "Limited Uthernet support is available on your system.\n\n" + "Install Npcap from https://npcap.com\n" + "or select Uthernet II with Virtual DNS."); + } + switch (m_tfe_selected) { case CT_Uthernet: @@ -206,6 +202,8 @@ void CPageConfigTfe::init_tfe_dialog(HWND hwnd) break; } + CheckDlgButton(hwnd, IDC_CHECK_TFE_VIRTUAL_DNS, m_tfe_virtual_dns ? BST_CHECKED : BST_UNCHECKED); + temp_hwnd=GetDlgItem(hwnd,IDC_TFE_SETTINGS_ENABLE); SendMessage(temp_hwnd, CB_ADDSTRING, 0, (LPARAM)"Disabled"); SendMessage(temp_hwnd, CB_ADDSTRING, 0, (LPARAM)"Uthernet"); @@ -244,22 +242,7 @@ void CPageConfigTfe::init_tfe_dialog(HWND hwnd) PCapBackend::tfe_enumadapter_close(); } - if (gray_ungray_items(hwnd)) - { - /* we have a problem: TFE is disabled. Give a message to the user */ - // TC (18 Dec 2017) this vicekb URL is a broken link now, so I copied it to the AppleWin repo, here: - // . https://github.com/AppleWin/AppleWin/blob/master/docs/VICE%20Knowledge%20Base%20-%20Article%2013-005.htm - MessageBox( hwnd, - "Uthernet support is not available on your system,\n" - "WPCAP.DLL cannot be loaded.\n\n" - "Install Npcap from\n\n" - " https://npcap.com\n\n" - "to activate networking with AppleWin.", - "Uthernet support", MB_ICONINFORMATION|MB_OK); - - /* just quit the dialog before it is open */ - SendMessage( hwnd, WM_COMMAND, IDCANCEL, 0); - } + gray_ungray_items(hwnd); } void CPageConfigTfe::save_tfe_dialog(HWND hwnd) @@ -270,27 +253,21 @@ void CPageConfigTfe::save_tfe_dialog(HWND hwnd) buffer[255] = 0; GetDlgItemText(hwnd, IDC_TFE_SETTINGS_INTERFACE, buffer, sizeof(buffer)-1); - // RGJ - Added check for NULL interface so we don't set it active without a valid interface selected - if (strlen(buffer) > 0) - { - m_tfe_interface_name = buffer; - active_value = SendMessage(GetDlgItem(hwnd, IDC_TFE_SETTINGS_ENABLE), CB_GETCURSEL, 0, 0); - switch (active_value) - { - case 1: - m_tfe_selected = CT_Uthernet; - break; - case 2: - m_tfe_selected = CT_Uthernet2; - break; - default: - m_tfe_selected = CT_Empty; - break; - } - } - else + m_tfe_interface_name = buffer; + + active_value = SendMessage(GetDlgItem(hwnd, IDC_TFE_SETTINGS_ENABLE), CB_GETCURSEL, 0, 0); + switch (active_value) { + case 1: + m_tfe_selected = CT_Uthernet; + break; + case 2: + m_tfe_selected = CT_Uthernet2; + break; + default: m_tfe_selected = CT_Empty; - m_tfe_interface_name.clear(); + break; } + + m_tfe_virtual_dns = IsDlgButtonChecked(hwnd, IDC_CHECK_TFE_VIRTUAL_DNS) ? 1 : 0; } diff --git a/source/Configuration/PageConfigTfe.h b/source/Configuration/PageConfigTfe.h index b2e6d812..a38ce221 100644 --- a/source/Configuration/PageConfigTfe.h +++ b/source/Configuration/PageConfigTfe.h @@ -13,6 +13,7 @@ public: { CPageConfigTfe::ms_this = this; m_tfe_selected = CT_Empty; + m_tfe_virtual_dns = false; } virtual ~CPageConfigTfe(){} @@ -20,6 +21,7 @@ public: SS_CARDTYPE m_tfe_selected; std::string m_tfe_interface_name; + bool m_tfe_virtual_dns; protected: // IPropertySheetPage @@ -29,7 +31,7 @@ protected: private: BOOL get_tfename(int number, std::string & name, std::string & description); - int gray_ungray_items(HWND hwnd); + void gray_ungray_items(HWND hwnd); void init_tfe_dialog(HWND hwnd); void save_tfe_dialog(HWND hwnd); diff --git a/source/Configuration/PageDisk.cpp b/source/Configuration/PageDisk.cpp index e95c2e26..01af5440 100644 --- a/source/Configuration/PageDisk.cpp +++ b/source/Configuration/PageDisk.cpp @@ -29,7 +29,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../Windows/AppleWin.h" #include "../CardManager.h" #include "../Disk.h" // Drive_e, Disk_Status_e +#include "../HardDisk.h" #include "../Registry.h" +#include "../Interface.h" #include "../resource/resource.h" CPageDisk* CPageDisk::ms_this = 0; // reinit'd in ctor diff --git a/source/Configuration/PropertySheetHelper.cpp b/source/Configuration/PropertySheetHelper.cpp index 475c7b44..c8c7a6cb 100644 --- a/source/Configuration/PropertySheetHelper.cpp +++ b/source/Configuration/PropertySheetHelper.cpp @@ -33,6 +33,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../Log.h" #include "../Registry.h" #include "../SaveState.h" +#include "../Interface.h" +#include "../Uthernet2.h" #include "../Tfe/PCapBackend.h" /* @@ -336,7 +338,8 @@ void CPropertySheetHelper::ApplyNewConfig(const CConfigNeedingRestart& ConfigNew SetSlot(slot, ConfigNew.m_Slot[slot]); // unconditionally save it, as the previous SetSlot might have removed the setting - PCapBackend::tfe_SetRegistryInterface(slot, ConfigNew.m_tfeInterface); + PCapBackend::SetRegistryInterface(slot, ConfigNew.m_tfeInterface); + Uthernet2::SetRegistryVirtualDNS(slot, ConfigNew.m_tfeVirtualDNS); slot = SLOT4; if (CONFIG_CHANGED_LOCAL(m_Slot[slot])) @@ -455,6 +458,9 @@ bool CPropertySheetHelper::HardwareConfigChanged(HWND hWnd) if (CONFIG_CHANGED(m_tfeInterface)) strMsgMain += ". Uthernet interface has changed\n"; + if (CONFIG_CHANGED(m_tfeVirtualDNS)) + strMsgMain += ". Uthernet Virtual DNS has changed\n"; + if (CONFIG_CHANGED(m_Slot[SLOT4])) strMsgMain += GetSlot(SLOT4); diff --git a/source/FrameBase.h b/source/FrameBase.h index c180d46d..fcf1c00e 100644 --- a/source/FrameBase.h +++ b/source/FrameBase.h @@ -45,7 +45,7 @@ public: // create the network backed for Uthernet 1 and 2 // useful to use libslirp in Linux - virtual std::shared_ptr CreateNetworkBackend() = 0; + virtual std::shared_ptr CreateNetworkBackend(const std::string & interfaceName) = 0; // FindResource, MAKEINTRESOURCE, SizeofResource, LoadResource, LockResource // Return pointer to resource if size is correct. diff --git a/source/SaveState.cpp b/source/SaveState.cpp index 61a408fc..2620b0a8 100644 --- a/source/SaveState.cpp +++ b/source/SaveState.cpp @@ -41,6 +41,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "Pravets.h" #include "Speaker.h" #include "Speech.h" +#include "HardDisk.h" #include "Configuration/Config.h" #include "Configuration/IPropertySheet.h" diff --git a/source/Tfe/DNS.cpp b/source/Tfe/DNS.cpp new file mode 100644 index 00000000..75a734cb --- /dev/null +++ b/source/Tfe/DNS.cpp @@ -0,0 +1,49 @@ +/* +AppleWin : An Apple //e emulator for Windows + +Copyright (C) 2022, Andrea Odetti + +AppleWin 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. + +AppleWin 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 AppleWin; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "StdAfx.h" + +#include "DNS.h" + +#ifndef _MSC_VER +#include +#include +#endif + +uint32_t getHostByName(const std::string & name) +{ + const hostent * host = gethostbyname(name.c_str()); + if (host && host->h_addrtype == AF_INET && host->h_length == sizeof(uint32_t)) + { + const in_addr * addr = (const in_addr *)host->h_addr_list[0]; + if (addr) + { + return addr->s_addr; + } + } + return 0; +} + +const char * formatIP(const uint32_t address) +{ + in_addr in; + in.s_addr = address; + return inet_ntoa(in); +} diff --git a/source/Tfe/DNS.h b/source/Tfe/DNS.h new file mode 100644 index 00000000..acc704ca --- /dev/null +++ b/source/Tfe/DNS.h @@ -0,0 +1,4 @@ +#pragma once + +uint32_t getHostByName(const std::string & name); +const char * formatIP(const uint32_t address); diff --git a/source/Tfe/NetworkBackend.h b/source/Tfe/NetworkBackend.h index 3bfda5dd..409836ed 100644 --- a/source/Tfe/NetworkBackend.h +++ b/source/Tfe/NetworkBackend.h @@ -39,4 +39,7 @@ public: // if the backend is usable virtual bool isValid() = 0; + + // get interface name + virtual const std::string & getInterfaceName() = 0; }; diff --git a/source/Tfe/PCapBackend.cpp b/source/Tfe/PCapBackend.cpp index 58d349da..be126175 100644 --- a/source/Tfe/PCapBackend.cpp +++ b/source/Tfe/PCapBackend.cpp @@ -29,17 +29,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #endif -std::string PCapBackend::tfe_interface; - -PCapBackend::PCapBackend(const std::string & pcapInterface) +PCapBackend::PCapBackend(const std::string & interfaceName) : m_interfaceName(interfaceName) { - tfePcapFP = TfePcapOpenAdapter(pcapInterface); + m_tfePcapFP = TfePcapOpenAdapter(interfaceName); } PCapBackend::~PCapBackend() { - TfePcapCloseAdapter(tfePcapFP); - tfePcapFP = NULL; + TfePcapCloseAdapter(m_tfePcapFP); + m_tfePcapFP = NULL; } void PCapBackend::transmit( @@ -47,17 +45,17 @@ void PCapBackend::transmit( uint8_t *txframe /* Pointer to the frame to be transmitted */ ) { - if (tfePcapFP) + if (m_tfePcapFP) { - tfe_arch_transmit(tfePcapFP, txlength, txframe); + tfe_arch_transmit(m_tfePcapFP, txlength, txframe); } } int PCapBackend::receive(const int size, uint8_t * rxframe) { - if (tfePcapFP) + if (m_tfePcapFP) { - return tfe_arch_receive(tfePcapFP, size, rxframe); + return tfe_arch_receive(m_tfePcapFP, size, rxframe); } else { @@ -67,7 +65,7 @@ int PCapBackend::receive(const int size, uint8_t * rxframe) bool PCapBackend::isValid() { - return tfePcapFP; + return m_tfePcapFP; } void PCapBackend::update(const ULONG /* nExecutedCycles */) @@ -85,6 +83,11 @@ void PCapBackend::getMACAddress(const uint32_t address, MACAddress & mac) #endif } +const std::string & PCapBackend::getInterfaceName() +{ + return m_interfaceName; +} + int PCapBackend::tfe_enumadapter_open(void) { return tfe_arch_enumadapter_open(); @@ -100,13 +103,26 @@ int PCapBackend::tfe_enumadapter_close(void) return tfe_arch_enumadapter_close(); } -void PCapBackend::tfe_SetRegistryInterface(UINT slot, const std::string& name) +const char * PCapBackend::tfe_lib_version(void) +{ + return tfe_arch_lib_version(); +} + +void PCapBackend::SetRegistryInterface(UINT slot, const std::string& name) { std::string regSection = RegGetConfigSlotSection(slot); RegSaveString(regSection.c_str(), REGVALUE_UTHERNET_INTERFACE, 1, name); } -void PCapBackend::get_disabled_state(int * param) +std::string PCapBackend::GetRegistryInterface(UINT slot) { - *param = tfe_cannot_use; + char interfaceName[MAX_PATH]; + std::string regSection = RegGetConfigSlotSection(slot); + RegLoadString(regSection.c_str(), REGVALUE_UTHERNET_INTERFACE, TRUE, interfaceName, sizeof(interfaceName), TEXT("")); + return interfaceName; +} + +int PCapBackend::tfe_is_npcap_loaded() +{ + return tfe_arch_is_npcap_loaded(); } diff --git a/source/Tfe/PCapBackend.h b/source/Tfe/PCapBackend.h index a94023d7..20cd42ff 100644 --- a/source/Tfe/PCapBackend.h +++ b/source/Tfe/PCapBackend.h @@ -10,7 +10,7 @@ typedef struct pcap pcap_t; class PCapBackend : public NetworkBackend { public: - PCapBackend(const std::string & pcapInterface); + PCapBackend(const std::string & interfaceName); virtual ~PCapBackend(); @@ -32,8 +32,12 @@ public: // get MAC for IPRAW (it is only supposed to handle addresses on the local network) virtual void getMACAddress(const uint32_t address, MACAddress & mac); - static void tfe_SetRegistryInterface(UINT slot, const std::string& name); - static void get_disabled_state(int * param); + // get interface name + virtual const std::string & getInterfaceName(); + + // global registry functions + static void SetRegistryInterface(UINT slot, const std::string& name); + static std::string GetRegistryInterface(UINT slot); /* These functions let the UI enumerate the available interfaces. @@ -59,9 +63,10 @@ public: static int tfe_enumadapter_open(void); static int tfe_enumadapter(std::string & name, std::string & description); static int tfe_enumadapter_close(void); - - static std::string tfe_interface; + static const char * tfe_lib_version(void); + static int tfe_is_npcap_loaded(); private: - pcap_t * tfePcapFP; + const std::string m_interfaceName; + pcap_t * m_tfePcapFP; }; diff --git a/source/Tfe/tfearch.cpp b/source/Tfe/tfearch.cpp index 193d4f9e..fe27dd04 100644 --- a/source/Tfe/tfearch.cpp +++ b/source/Tfe/tfearch.cpp @@ -28,6 +28,11 @@ /* #define WPCAP */ #ifdef _MSC_VER + +#ifndef NOMINMAX +#define NOMINMAX +#endif + #include "pcap.h" #else // on Linux and Mac OS X, we use system's pcap.h, which needs to be included as <> @@ -51,7 +56,8 @@ #define TFE_DEBUG_WARN 1 /* this should not be deactivated */ -int tfe_cannot_use = 0; +// once this is set, no further attempts to load npcap will be made +static int tfe_cannot_use = 0; #ifdef _MSC_VER @@ -113,36 +119,48 @@ void TfePcapFreeLibrary(void) static BOOL TfePcapLoadLibrary(void) { - if (!pcap_library) { - if (!SetDllDirectory("C:\\Windows\\System32\\Npcap\\")) // Prefer Npcap over WinPcap (GH#822) - { - const char* error = "Warning: SetDllDirectory() failed for Npcap"; - LogOutput("%s\n", error); - LogFileOutput("%s\n", error); - } - - pcap_library = LoadLibrary("wpcap.dll"); - - if (!pcap_library) { - tfe_cannot_use = 1; - if(g_fh) fprintf(g_fh, "LoadLibrary WPCAP.DLL failed!\n" ); - return FALSE; - } - - GET_PROC_ADDRESS_AND_TEST(pcap_open_live); - GET_PROC_ADDRESS_AND_TEST(pcap_close); - GET_PROC_ADDRESS_AND_TEST(pcap_dispatch); - GET_PROC_ADDRESS_AND_TEST(pcap_setnonblock); - GET_PROC_ADDRESS_AND_TEST(pcap_findalldevs); - GET_PROC_ADDRESS_AND_TEST(pcap_freealldevs); - GET_PROC_ADDRESS_AND_TEST(pcap_sendpacket); - GET_PROC_ADDRESS_AND_TEST(pcap_datalink); - GET_PROC_ADDRESS_AND_TEST(pcap_lib_version); - GET_PROC_ADDRESS_AND_TEST(pcap_geterr); - LogOutput("%s\n", p_pcap_lib_version()); - LogFileOutput("%s\n", p_pcap_lib_version()); + if (pcap_library) + { + // already loaded + return TRUE; } + if (tfe_cannot_use) + { + // already failed + return FALSE; + } + + // try to load + if (!SetDllDirectory("C:\\Windows\\System32\\Npcap\\")) // Prefer Npcap over WinPcap (GH#822) + { + const char* error = "Warning: SetDllDirectory() failed for Npcap"; + LogOutput("%s\n", error); + LogFileOutput("%s\n", error); + } + + pcap_library = LoadLibrary("wpcap.dll"); + + if (!pcap_library) + { + tfe_cannot_use = 1; + if(g_fh) fprintf(g_fh, "LoadLibrary WPCAP.DLL failed!\n" ); + return FALSE; + } + + GET_PROC_ADDRESS_AND_TEST(pcap_open_live); + GET_PROC_ADDRESS_AND_TEST(pcap_close); + GET_PROC_ADDRESS_AND_TEST(pcap_dispatch); + GET_PROC_ADDRESS_AND_TEST(pcap_setnonblock); + GET_PROC_ADDRESS_AND_TEST(pcap_findalldevs); + GET_PROC_ADDRESS_AND_TEST(pcap_freealldevs); + GET_PROC_ADDRESS_AND_TEST(pcap_sendpacket); + GET_PROC_ADDRESS_AND_TEST(pcap_datalink); + GET_PROC_ADDRESS_AND_TEST(pcap_lib_version); + GET_PROC_ADDRESS_AND_TEST(pcap_geterr); + LogOutput("%s\n", p_pcap_lib_version()); + LogFileOutput("%s\n", p_pcap_lib_version()); + return TRUE; } @@ -438,7 +456,7 @@ void TfePcapPacketHandler(u_char *param, const struct pcap_pkthdr *header, const /* determine the count of bytes which has been returned, * but make sure not to overrun the buffer */ - pinternal->rxlength = min(pinternal->size, header->caplen); + pinternal->rxlength = std::min(pinternal->size, header->caplen); memcpy(pinternal->buffer, pkt_data, pinternal->rxlength); } @@ -551,4 +569,19 @@ int tfe_arch_receive(pcap_t * TfePcapFP, return -1; } +const char * tfe_arch_lib_version() +{ + if (!TfePcapLoadLibrary()) + { + return 0; + } + + return p_pcap_lib_version(); +} + +int tfe_arch_is_npcap_loaded() +{ + return TfePcapLoadLibrary(); +} + //#endif /* #ifdef HAVE_TFE */ diff --git a/source/Tfe/tfearch.h b/source/Tfe/tfearch.h index 7f7f270e..45988a10 100644 --- a/source/Tfe/tfearch.h +++ b/source/Tfe/tfearch.h @@ -34,9 +34,6 @@ extern void tfe_arch_set_mac(const BYTE mac[6]); extern void tfe_arch_set_hashfilter(const DWORD hash_mask[2]); -/* Flag: Can we even use TFE, or is the hardware not available? */ -extern int tfe_cannot_use; - struct pcap; typedef struct pcap pcap_t; @@ -67,8 +64,11 @@ int tfe_arch_receive(pcap_t * TfePcapFP, BYTE *pbuffer /* where to store a frame */ ); +extern int tfe_arch_is_npcap_loaded(); extern int tfe_arch_enumadapter_open(void); extern int tfe_arch_enumadapter(std::string & name, std::string & description); extern int tfe_arch_enumadapter_close(void); +extern const char * tfe_arch_lib_version(); + #endif diff --git a/source/Uthernet1.cpp b/source/Uthernet1.cpp index 0aa62ece..1c9707e3 100644 --- a/source/Uthernet1.cpp +++ b/source/Uthernet1.cpp @@ -1011,7 +1011,8 @@ static BYTE __stdcall TfeIo (WORD programcounter, WORD address, BYTE write, BYTE void Uthernet1::InitializeIO(LPBYTE pCxRomPeripheral) { - networkBackend = GetFrame().CreateNetworkBackend(); + const std::string interfaceName = PCapBackend::GetRegistryInterface(m_slot); + networkBackend = GetFrame().CreateNetworkBackend(interfaceName); if (networkBackend->isValid()) { RegisterIoHandler(m_slot, TfeIo, TfeIo, TfeIoCxxx, TfeIoCxxx, this, NULL); @@ -1058,10 +1059,10 @@ void Uthernet1::SaveSnapshot(class YamlSaveHelper& yamlSaveHelper) YamlSaveHelper::Label unit(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE); yamlSaveHelper.SaveBool(SS_YAML_KEY_ENABLED, networkBackend->isValid() ? true : false); - yamlSaveHelper.SaveString(SS_YAML_KEY_NETWORK_INTERFACE, PCapBackend::tfe_interface); + yamlSaveHelper.SaveString(SS_YAML_KEY_NETWORK_INTERFACE, networkBackend->getInterfaceName()); yamlSaveHelper.SaveBool(SS_YAML_KEY_STARTED_TX, tfe_started_tx ? true : false); - yamlSaveHelper.SaveBool(SS_YAML_KEY_CANNOT_USE, tfe_cannot_use ? true : false); + yamlSaveHelper.SaveBool(SS_YAML_KEY_CANNOT_USE, PCapBackend::tfe_is_npcap_loaded() ? false : false); yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_TXCOLLECT_BUFFER, txcollect_buffer); yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_RX_BUFFER, rx_buffer); @@ -1083,10 +1084,13 @@ bool Uthernet1::LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT version) ThrowErrorInvalidVersion(version); yamlLoadHelper.LoadBool(SS_YAML_KEY_ENABLED); // FIXME: what is the point of this? - PCapBackend::tfe_interface = yamlLoadHelper.LoadString(SS_YAML_KEY_NETWORK_INTERFACE); + PCapBackend::SetRegistryInterface(m_slot, yamlLoadHelper.LoadString(SS_YAML_KEY_NETWORK_INTERFACE)); tfe_started_tx = yamlLoadHelper.LoadBool(SS_YAML_KEY_STARTED_TX) ? true : false; - tfe_cannot_use = yamlLoadHelper.LoadBool(SS_YAML_KEY_CANNOT_USE) ? true : false; + + // it is meaningless to restore this boolean flag + // as it depends on the availability of npcap on *this* pc + const bool tfe_cannot_use = yamlLoadHelper.LoadBool(SS_YAML_KEY_CANNOT_USE); txcollect_buffer = yamlLoadHelper.LoadUint(SS_YAML_KEY_TXCOLLECT_BUFFER); rx_buffer = yamlLoadHelper.LoadUint(SS_YAML_KEY_RX_BUFFER); @@ -1117,7 +1121,5 @@ bool Uthernet1::LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT version) for (UINT i = 0; i < 6; i++) tfe_sideeffects_write_pp((TFE_PP_ADDR_MAC_ADDR + i) & ~1, i & 1); // set tfe_ia_mac - PCapBackend::tfe_SetRegistryInterface(m_slot, PCapBackend::tfe_interface); - return true; } diff --git a/source/Uthernet2.cpp b/source/Uthernet2.cpp index 79970f52..9b122af6 100644 --- a/source/Uthernet2.cpp +++ b/source/Uthernet2.cpp @@ -25,7 +25,18 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "Tfe/NetworkBackend.h" #include "Tfe/PCapBackend.h" #include "Tfe/IPRaw.h" +#include "Tfe/DNS.h" #include "W5100.h" +#include "../Registry.h" + + +// Virtual DNS +// Virtual DNS is an extension to the W5100 +// It enables DNS resolution by setting P3 = 1 for IP / TCP and UDP +// the length-prefixed hostname is in 0x2A-0xFF is each socket memory +// it can be identified with PTIMER = 0. +// this means, one can use TCP & UDP without a NetworkBackend. + // Linux uses EINPROGRESS while Windows returns WSAEWOULDBLOCK // when the connect() calls is ongoing @@ -297,7 +308,6 @@ bool Socket::LoadSnapshot(YamlLoadHelper &yamlLoadHelper) sn_sr = yamlLoadHelper.LoadUint(SS_YAML_KEY_SOCKET_REGISTER); // transmit and receive sizes are restored from the card common registers - switch (sn_sr) { case W5100_SN_SR_SOCK_MACRAW: @@ -323,9 +333,30 @@ const std::string& Uthernet2::GetSnapshotCardName() Uthernet2::Uthernet2(UINT slot) : Card(CT_Uthernet2, slot) { +#ifdef _MSC_VER + WSADATA wsaData; + myWSAStartup = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (myWSAStartup) + { + const int error = sock_error(); + LogFileOutput("U2: WSAStartup: error %" ERROR_FMT "\n", STRERROR(error)); + } +#endif + + myVirtualDNSEnabled = GetRegistryVirtualDNS(slot); Reset(true); } +Uthernet2::~Uthernet2() +{ +#ifdef _MSC_VER + if (myWSAStartup == 0) + { + WSACleanup(); + } +#endif +} + void Uthernet2::setSocketModeRegister(const size_t i, const uint16_t address, const uint8_t value) { myMemory[address] = value; @@ -338,16 +369,19 @@ void Uthernet2::setSocketModeRegister(const size_t i, const uint16_t address, co #endif break; case W5100_SN_MR_TCP: + case W5100_SN_MR_TCP_DNS: #ifdef U2_LOG_STATE LogFileOutput("U2: Mode[%" SIZE_T_FMT "]: TCP\n", i); #endif break; case W5100_SN_MR_UDP: + case W5100_SN_MR_UDP_DNS: #ifdef U2_LOG_STATE LogFileOutput("U2: Mode[%" SIZE_T_FMT "]: UDP\n", i); #endif break; case W5100_SN_MR_IPRAW: + case W5100_SN_MR_IPRAW_DNS: #ifdef U2_LOG_STATE LogFileOutput("U2: Mode[%" SIZE_T_FMT "]: IPRAW\n", i); #endif @@ -698,13 +732,13 @@ void Uthernet2::sendDataIPRaw(const size_t i, std::vector &payload) const uint8_t tos = myMemory[socket.registerAddress + W5100_SN_TOS]; const uint8_t protocol = myMemory[socket.registerAddress + W5100_SN_PROTO]; const uint32_t source = readAddress(myMemory.data() + W5100_SIPR0); - const uint32_t destination = readAddress(myMemory.data() + socket.registerAddress + W5100_SN_DIPR0); + const uint32_t dest = readAddress(myMemory.data() + socket.registerAddress + W5100_SN_DIPR0); const MACAddress * sourceMac = reinterpret_cast(myMemory.data() + W5100_SHAR0); const MACAddress * destinationMac; - getMACAddress(destination, destinationMac); + getMACAddress(dest, destinationMac); - std::vector packet = createETH2Frame(payload, sourceMac, destinationMac, ttl, tos, protocol, source, destination); + std::vector packet = createETH2Frame(payload, sourceMac, destinationMac, ttl, tos, protocol, source, dest); #ifdef U2_LOG_TRAFFIC LogFileOutput("U2: Send IPRAW[%" SIZE_T_FMT "]: %" SIZE_T_FMT " (%" SIZE_T_FMT ") bytes\n", i, payload.size(), packet.size()); @@ -738,10 +772,9 @@ void Uthernet2::sendDataToSocket(const size_t i, std::vector &data) sockaddr_in destination = {}; destination.sin_family = AF_INET; - // already in network order // this seems to be ignored for TCP, and so we reuse the same code - const uint8_t *dest = myMemory.data() + socket.registerAddress + W5100_SN_DIPR0; - destination.sin_addr.s_addr = *reinterpret_cast(dest); + const uint32_t dest = readAddress(myMemory.data() + socket.registerAddress + W5100_SN_DIPR0); + destination.sin_addr.s_addr = dest; destination.sin_port = *reinterpret_cast(myMemory.data() + socket.registerAddress + W5100_SN_DPORT0); const ssize_t res = sendto(socket.myFD, reinterpret_cast(data.data()), data.size(), 0, (const struct sockaddr *)&destination, sizeof(destination)); @@ -853,21 +886,38 @@ void Uthernet2::openSystemSocket(const size_t i, const int type, const int proto void Uthernet2::openSocket(const size_t i) { Socket &socket = mySockets[i]; + socket.clearFD(); + const uint8_t mr = myMemory[socket.registerAddress + W5100_SN_MR]; const uint8_t protocol = mr & W5100_SN_MR_PROTO_MASK; + const bool virtual_dns = protocol & W5100_SN_VIRTUAL_DNS; + + // if virtual_dns is requested, but not enabled, we cannot handle it here. + if (virtual_dns && !myVirtualDNSEnabled) + { +#ifdef U2_LOG_STATE + LogFileOutput("U2: Open[%" SIZE_T_FMT "]: virtual DNS not supported: %02x\n", i, mr); +#endif + return; + } + uint8_t &sr = socket.sn_sr; + switch (protocol) { case W5100_SN_MR_IPRAW: + case W5100_SN_MR_IPRAW_DNS: sr = W5100_SN_SR_SOCK_IPRAW; break; case W5100_SN_MR_MACRAW: sr = W5100_SN_SR_SOCK_MACRAW; break; case W5100_SN_MR_TCP: + case W5100_SN_MR_TCP_DNS: openSystemSocket(i, SOCK_STREAM, IPPROTO_TCP, W5100_SN_SR_SOCK_INIT); break; case W5100_SN_MR_UDP: + case W5100_SN_MR_UDP_DNS: openSystemSocket(i, SOCK_DGRAM, IPPROTO_UDP, W5100_SN_SR_SOCK_UDP); break; #ifdef U2_LOG_UNKNOWN @@ -875,6 +925,16 @@ void Uthernet2::openSocket(const size_t i) LogFileOutput("U2: Open[%" SIZE_T_FMT "]: unknown mode: %02x\n", i, mr); #endif } + + switch (protocol) + { + case W5100_SN_MR_IPRAW_DNS: + case W5100_SN_MR_TCP_DNS: + case W5100_SN_MR_UDP_DNS: + resolveDNS(i); + break; + } + resetRXTXBuffers(i); // needed? #ifdef U2_LOG_STATE LogFileOutput("U2: Open[%" SIZE_T_FMT "]: SR = %02x\n", i, sr); @@ -890,17 +950,44 @@ void Uthernet2::closeSocket(const size_t i) #endif } +void Uthernet2::resolveDNS(const size_t i) +{ + Socket &socket = mySockets[i]; + uint32_t *dest = reinterpret_cast(myMemory.data() + socket.registerAddress + W5100_SN_DIPR0); + *dest = 0; // 0.0.0.0 signals failure by any reason + + const uint8_t length = myMemory[socket.registerAddress + W5100_SN_DNS_NAME_LEN]; + if (length <= W5100_SN_DNS_NAME_CPTY) + { + const uint8_t * start = myMemory.data() + socket.registerAddress + W5100_SN_DNS_NAME_BEGIN; + const std::string name(start, start + length); + const std::map::iterator it = myDNSCache.find(name); + if (it != myDNSCache.end()) + { + *dest = it->second; + } + else + { + *dest = getHostByName(name); + myDNSCache[name] = *dest; +#ifdef U2_LOG_STATE + LogFileOutput("U2: DNS[%" SIZE_T_FMT "]: %s = %s\n", i, name.c_str(), formatIP(*dest)); +#endif + } + } +} + void Uthernet2::connectSocket(const size_t i) { Socket &socket = mySockets[i]; - const uint8_t *dest = myMemory.data() + socket.registerAddress + W5100_SN_DIPR0; + const uint32_t dest = readAddress(myMemory.data() + socket.registerAddress + W5100_SN_DIPR0); sockaddr_in destination = {}; destination.sin_family = AF_INET; // already in network order destination.sin_port = *reinterpret_cast(myMemory.data() + socket.registerAddress + W5100_SN_DPORT0); - destination.sin_addr.s_addr = *reinterpret_cast(dest); + destination.sin_addr.s_addr = dest; const int res = connect(socket.myFD, (struct sockaddr *)&destination, sizeof(destination)); @@ -910,7 +997,7 @@ void Uthernet2::connectSocket(const size_t i) socket.myErrno = 0; #ifdef U2_LOG_STATE const uint16_t port = readNetworkWord(myMemory.data() + socket.registerAddress + W5100_SN_DPORT0); - LogFileOutput("U2: TCP[%" SIZE_T_FMT "]: CONNECT to %d.%d.%d.%d:%d\n", i, dest[0], dest[1], dest[2], dest[3], port); + LogFileOutput("U2: TCP[%" SIZE_T_FMT "]: CONNECT to %s:%d\n", i, formatIP(dest), port); #endif } else @@ -1130,11 +1217,18 @@ void Uthernet2::writeSocketRegister(const uint16_t address, const uint8_t value) case W5100_SN_RX_RD1: myMemory[address] = value; break; -#ifdef U2_LOG_UNKNOWN default: - LogFileOutput("U2: Set unknown socket register[%d]: %04x\n", i, address); - break; + if (myVirtualDNSEnabled && loc >= W5100_SN_DNS_NAME_LEN) + { + myMemory[address] = value; + } + else + { +#ifdef U2_LOG_UNKNOWN + LogFileOutput("U2: Set unknown socket register[%d]: %04x\n", i, address); #endif + } + break; }; } @@ -1216,8 +1310,10 @@ void Uthernet2::Reset(const bool powerCycle) { // dataAddress is NOT reset, see page 10 of Uthernet II myDataAddress = 0; - myNetworkBackend = GetFrame().CreateNetworkBackend(); + const std::string interfaceName = PCapBackend::GetRegistryInterface(m_slot); + myNetworkBackend = GetFrame().CreateNetworkBackend(interfaceName); myARPCache.clear(); + myDNSCache.clear(); } mySockets.clear(); @@ -1247,7 +1343,11 @@ void Uthernet2::Reset(const bool powerCycle) myMemory[W5100_RCR] = 0x08; setRXSizes(W5100_RMSR, 0x55); setTXSizes(W5100_TMSR, 0x55); - myMemory[W5100_PTIMER] = 0x28; + if (!myVirtualDNSEnabled) + { + // this is 0 if we support Virtual DNS + myMemory[W5100_PTIMER] = 0x28; + } } BYTE Uthernet2::IO_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles) @@ -1367,7 +1467,11 @@ void Uthernet2::Update(const ULONG nExecutedCycles) } } -static const UINT kUNIT_VERSION = 1; +// Unit version history: +// 2: Added: Virtual DNS +static const UINT kUNIT_VERSION = 2; + +#define SS_YAML_KEY_VIRTUAL_DNS "Virtual DNS" #define SS_YAML_KEY_NETWORK_INTERFACE "Network Interface" #define SS_YAML_KEY_COMMON_REGISTERS "Common Registers" @@ -1385,7 +1489,8 @@ void Uthernet2::SaveSnapshot(YamlSaveHelper &yamlSaveHelper) YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_VERSION); YamlSaveHelper::Label unit(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE); - yamlSaveHelper.SaveString(SS_YAML_KEY_NETWORK_INTERFACE, PCapBackend::tfe_interface); + yamlSaveHelper.SaveString(SS_YAML_KEY_NETWORK_INTERFACE, myNetworkBackend->getInterfaceName()); + yamlSaveHelper.SaveBool(SS_YAML_KEY_VIRTUAL_DNS, myVirtualDNSEnabled); yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_MODE_REGISTER, myModeRegister); yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_DATA_ADDRESS, myDataAddress); @@ -1428,10 +1533,19 @@ bool Uthernet2::LoadSnapshot(YamlLoadHelper &yamlLoadHelper, UINT version) if (version < 1 || version > kUNIT_VERSION) ThrowErrorInvalidVersion(version); - PCapBackend::tfe_interface = yamlLoadHelper.LoadString(SS_YAML_KEY_NETWORK_INTERFACE); - PCapBackend::tfe_SetRegistryInterface(m_slot, PCapBackend::tfe_interface); + PCapBackend::SetRegistryInterface(m_slot, yamlLoadHelper.LoadString(SS_YAML_KEY_NETWORK_INTERFACE)); - Reset(true); // AFTER the interface name has been restored + if (version >= 2) + { + myVirtualDNSEnabled = yamlLoadHelper.LoadBool(SS_YAML_KEY_VIRTUAL_DNS); + } + else + { + myVirtualDNSEnabled = false; + } + SetRegistryVirtualDNS(m_slot, myVirtualDNSEnabled); + + Reset(true); // AFTER the parameters have been restored myModeRegister = yamlLoadHelper.LoadUint(SS_YAML_KEY_MODE_REGISTER); myDataAddress = yamlLoadHelper.LoadUint(SS_YAML_KEY_DATA_ADDRESS); @@ -1475,3 +1589,17 @@ bool Uthernet2::LoadSnapshot(YamlLoadHelper &yamlLoadHelper, UINT version) return true; } + +void Uthernet2::SetRegistryVirtualDNS(UINT slot, const bool enabled) +{ + const std::string regSection = RegGetConfigSlotSection(slot); + RegSaveValue(regSection.c_str(), REGVALUE_UTHERNET_VIRTUAL_DNS, TRUE, enabled); +} + +bool Uthernet2::GetRegistryVirtualDNS(UINT slot) +{ + const std::string regSection = RegGetConfigSlotSection(slot); + DWORD enabled = 0; + RegLoadValue(regSection.c_str(), REGVALUE_UTHERNET_VIRTUAL_DNS, TRUE, &enabled, 0); + return enabled != 0; +} diff --git a/source/Uthernet2.h b/source/Uthernet2.h index 8b33af21..fc4dd5af 100644 --- a/source/Uthernet2.h +++ b/source/Uthernet2.h @@ -61,6 +61,7 @@ public: enum PacketDestination { HOST, BROADCAST, OTHER }; Uthernet2(UINT slot); + virtual ~Uthernet2(); virtual void Destroy(void) {} virtual void InitializeIO(LPBYTE pCxRomPeripheral); @@ -71,7 +72,17 @@ public: BYTE IO_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles); + // global registry functions + static void SetRegistryVirtualDNS(UINT slot, const bool enabled); + static bool GetRegistryVirtualDNS(UINT slot); + private: + bool myVirtualDNSEnabled; // extended virtualisation of DNS (not present in the real U II card) + +#ifdef _MSC_VER + int myWSAStartup; +#endif + std::vector myMemory; std::vector mySockets; uint8_t myModeRegister; @@ -82,8 +93,10 @@ private: // but in the interest of speeding up the emulator // we introduce one std::map myARPCache; + std::map myDNSCache; void getMACAddress(const uint32_t address, const MACAddress * & mac); + void resolveDNS(const size_t i); void setSocketModeRegister(const size_t i, const uint16_t address, const uint8_t value); void setTXSizes(const uint16_t address, uint8_t value); diff --git a/source/Utilities.cpp b/source/Utilities.cpp index 8afb38a7..44bd1d8e 100644 --- a/source/Utilities.cpp +++ b/source/Utilities.cpp @@ -239,27 +239,15 @@ void LoadConfiguration(bool loadImages) if (RegLoadValue(regSection.c_str(), REGVALUE_CARD_TYPE, TRUE, &dwTmp)) { - if (slot == SLOT3) - { - // this must happen before the card is instantitated - // TODO move to the card - if ((SS_CARDTYPE)dwTmp == CT_Uthernet || (SS_CARDTYPE)dwTmp == CT_Uthernet2) // TODO: move this to when UthernetCard object is instantiated - { - std::string regSection = RegGetConfigSlotSection(slot); - if (RegLoadString(regSection.c_str(), REGVALUE_UTHERNET_INTERFACE, TRUE, szFilename, MAX_PATH, TEXT(""))) - PCapBackend::tfe_interface = szFilename; - } - } - GetCardMgr().Insert(slot, (SS_CARDTYPE)dwTmp, false); } else // legacy (AppleWin 1.30.3 or earlier) { if (slot == SLOT3) { - // TODO: move this to when UthernetCard object is instantiated RegLoadString(TEXT(REG_CONFIG), TEXT(REGVALUE_UTHERNET_INTERFACE), 1, szFilename, MAX_PATH, TEXT("")); - PCapBackend::tfe_interface = szFilename; + // copy it to the new location + PCapBackend::SetRegistryInterface(slot, szFilename); DWORD tfeEnabled; REGLOAD_DEFAULT(TEXT(REGVALUE_UTHERNET_ACTIVE), &tfeEnabled, 0); diff --git a/source/W5100.h b/source/W5100.h index c07a8502..23ff1dc6 100644 --- a/source/W5100.h +++ b/source/W5100.h @@ -46,6 +46,10 @@ #define W5100_SN_MR_IPRAW 0x03 #define W5100_SN_MR_MACRAW 0x04 #define W5100_SN_MR_PPPOE 0x05 +#define W5100_SN_VIRTUAL_DNS 0x08 // not present on real hardware, see comment in Uthernet2.cpp +#define W5100_SN_MR_TCP_DNS (W5100_SN_VIRTUAL_DNS | W5100_SN_MR_TCP) +#define W5100_SN_MR_UDP_DNS (W5100_SN_VIRTUAL_DNS | W5100_SN_MR_UDP) +#define W5100_SN_MR_IPRAW_DNS (W5100_SN_VIRTUAL_DNS | W5100_SN_MR_IPRAW) #define W5100_SN_CR_OPEN 0x01 #define W5100_SN_CR_LISTENT 0x02 @@ -88,6 +92,11 @@ #define W5100_SN_RX_RD0 0x28 // RX Read Pointer #define W5100_SN_RX_RD1 0x29 // RX Read Pointer +#define W5100_SN_DNS_NAME_LEN 0x2A // these are not present on real hardware, see comment in Uthernet2.cpp +#define W5100_SN_DNS_NAME_BEGIN 0x2B +#define W5100_SN_DNS_NAME_END 0xFF +#define W5100_SN_DNS_NAME_CPTY (W5100_SN_DNS_NAME_END - W5100_SN_DNS_NAME_BEGIN) + #define W5100_SN_SR_CLOSED 0x00 #define W5100_SN_SR_SOCK_INIT 0x13 #define W5100_SN_SR_ESTABLISHED 0x17 diff --git a/source/Windows/AppleWin.cpp b/source/Windows/AppleWin.cpp index 7af87e25..ec19a418 100644 --- a/source/Windows/AppleWin.cpp +++ b/source/Windows/AppleWin.cpp @@ -46,6 +46,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "SoundCore.h" #include "Speaker.h" #include "LanguageCard.h" +#include "CardManager.h" #ifdef USE_SPEECH_API #include "Speech.h" #endif diff --git a/source/Windows/Win32Frame.cpp b/source/Windows/Win32Frame.cpp index ed3e5800..e0918df5 100644 --- a/source/Windows/Win32Frame.cpp +++ b/source/Windows/Win32Frame.cpp @@ -625,8 +625,8 @@ std::string Win32Frame::Video_GetScreenShotFolder() const return std::string(); } -std::shared_ptr Win32Frame::CreateNetworkBackend() +std::shared_ptr Win32Frame::CreateNetworkBackend(const std::string & interfaceName) { - std::shared_ptr backend(new PCapBackend(PCapBackend::tfe_interface)); + std::shared_ptr backend(new PCapBackend(interfaceName)); return backend; } diff --git a/source/Windows/Win32Frame.h b/source/Windows/Win32Frame.h index 63853ef1..4f8c1183 100644 --- a/source/Windows/Win32Frame.h +++ b/source/Windows/Win32Frame.h @@ -57,7 +57,7 @@ public: virtual std::string Video_GetScreenShotFolder() const; - virtual std::shared_ptr CreateNetworkBackend(); + virtual std::shared_ptr CreateNetworkBackend(const std::string & interfaceName); bool GetFullScreenShowSubunitStatus(void); int GetFullScreenOffsetX(void); diff --git a/source/Windows/WinFrame.cpp b/source/Windows/WinFrame.cpp index decad356..53c6f7e3 100644 --- a/source/Windows/WinFrame.cpp +++ b/source/Windows/WinFrame.cpp @@ -48,6 +48,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "Uthernet2.h" #include "Speaker.h" #include "Utilities.h" +#include "CardManager.h" #include "../resource/resource.h" #include "Configuration/PropertySheet.h" #include "Debugger/Debug.h"