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.
This commit is contained in:
Andrea 2022-05-08 17:26:01 +02:00 committed by GitHub
parent ccb4582bf9
commit 6a5ea92a4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 539 additions and 226 deletions

View File

@ -114,6 +114,7 @@
<ClInclude Include="source\StrFormat.h" />
<ClInclude Include="source\SynchronousEventManager.h" />
<ClInclude Include="source\Tape.h" />
<ClInclude Include="source\Tfe\DNS.h" />
<ClInclude Include="source\Tfe\IPRaw.h" />
<ClInclude Include="source\Tfe\NetworkBackend.h" />
<ClInclude Include="source\Tfe\Bpf.h" />
@ -157,6 +158,7 @@
<ClCompile Include="source\CardManager.cpp" />
<ClCompile Include="source\CmdLine.cpp" />
<ClCompile Include="source\Configuration\About.cpp" />
<ClCompile Include="source\Configuration\Config.cpp" />
<ClCompile Include="source\Configuration\PageAdvanced.cpp" />
<ClCompile Include="source\Configuration\PageConfig.cpp" />
<ClCompile Include="source\Configuration\PageConfigTfe.cpp" />
@ -223,6 +225,7 @@
<ClCompile Include="source\StrFormat.cpp" />
<ClCompile Include="source\SynchronousEventManager.cpp" />
<ClCompile Include="source\Tape.cpp" />
<ClCompile Include="source\Tfe\DNS.cpp" />
<ClCompile Include="source\Tfe\IPRaw.cpp" />
<ClCompile Include="source\Tfe\NetworkBackend.cpp" />
<ClCompile Include="source\Tfe\PCapBackend.cpp" />

View File

@ -262,6 +262,12 @@
<ClCompile Include="source\Tfe\IPRaw.cpp">
<Filter>Source Files\Uthernet</Filter>
</ClCompile>
<ClCompile Include="source\Tfe\DNS.cpp">
<Filter>Source Files\Uthernet</Filter>
</ClCompile>
<ClCompile Include="source\Configuration\Config.cpp">
<Filter>Source Files\Configuration</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="source\CommonVICE\6510core.h">
@ -600,6 +606,9 @@
<ClInclude Include="source\Tfe\IPRaw.h">
<Filter>Source Files\Uthernet</Filter>
</ClInclude>
<ClInclude Include="source\Tfe\DNS.h">
<Filter>Source Files\Uthernet</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="resource\Applewin.bmp">

View File

@ -97,5 +97,8 @@
<li>after loading a save-state file, TCP and UDP sockets are closed</li>
</ul>
</P>
<P>The card implements a <A href="https://github.com/a2retrosystems/uthernet2/wiki/Virtual-W5100-with-DNS">Virtual DNS</A>
interface (not found on real hardware) for Apple II software to run without raw sockets:
this allows operation on any type of network.</P>
</body>
</html>

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -45,7 +45,7 @@ public:
// create the network backed for Uthernet 1 and 2
// useful to use libslirp in Linux
virtual std::shared_ptr<NetworkBackend> CreateNetworkBackend() = 0;
virtual std::shared_ptr<NetworkBackend> CreateNetworkBackend(const std::string & interfaceName) = 0;
// FindResource, MAKEINTRESOURCE, SizeofResource, LoadResource, LockResource
// Return pointer to resource if size is correct.

View File

@ -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"

49
source/Tfe/DNS.cpp Normal file
View File

@ -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 <arpa/inet.h>
#include <netdb.h>
#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);
}

4
source/Tfe/DNS.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
uint32_t getHostByName(const std::string & name);
const char * formatIP(const uint32_t address);

View File

@ -39,4 +39,7 @@ public:
// if the backend is usable
virtual bool isValid() = 0;
// get interface name
virtual const std::string & getInterfaceName() = 0;
};

View File

@ -29,17 +29,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include <iphlpapi.h>
#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();
}

View File

@ -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;
};

View File

@ -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 */

View File

@ -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

View File

@ -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;
}

View File

@ -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<uint8_t> &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<const MACAddress *>(myMemory.data() + W5100_SHAR0);
const MACAddress * destinationMac;
getMACAddress(destination, destinationMac);
getMACAddress(dest, destinationMac);
std::vector<uint8_t> packet = createETH2Frame(payload, sourceMac, destinationMac, ttl, tos, protocol, source, destination);
std::vector<uint8_t> 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<uint8_t> &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<const uint32_t *>(dest);
const uint32_t dest = readAddress(myMemory.data() + socket.registerAddress + W5100_SN_DIPR0);
destination.sin_addr.s_addr = dest;
destination.sin_port = *reinterpret_cast<const uint16_t *>(myMemory.data() + socket.registerAddress + W5100_SN_DPORT0);
const ssize_t res = sendto(socket.myFD, reinterpret_cast<const char *>(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<uint32_t *>(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<std::string, uint32_t>::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<const uint16_t *>(myMemory.data() + socket.registerAddress + W5100_SN_DPORT0);
destination.sin_addr.s_addr = *reinterpret_cast<const uint32_t *>(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;
}

View File

@ -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<uint8_t> myMemory;
std::vector<Socket> mySockets;
uint8_t myModeRegister;
@ -82,8 +93,10 @@ private:
// but in the interest of speeding up the emulator
// we introduce one
std::map<uint32_t, MACAddress> myARPCache;
std::map<std::string, uint32_t> 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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -625,8 +625,8 @@ std::string Win32Frame::Video_GetScreenShotFolder() const
return std::string();
}
std::shared_ptr<NetworkBackend> Win32Frame::CreateNetworkBackend()
std::shared_ptr<NetworkBackend> Win32Frame::CreateNetworkBackend(const std::string & interfaceName)
{
std::shared_ptr<NetworkBackend> backend(new PCapBackend(PCapBackend::tfe_interface));
std::shared_ptr<NetworkBackend> backend(new PCapBackend(interfaceName));
return backend;
}

View File

@ -57,7 +57,7 @@ public:
virtual std::string Video_GetScreenShotFolder() const;
virtual std::shared_ptr<NetworkBackend> CreateNetworkBackend();
virtual std::shared_ptr<NetworkBackend> CreateNetworkBackend(const std::string & interfaceName);
bool GetFullScreenShowSubunitStatus(void);
int GetFullScreenOffsetX(void);

View File

@ -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"