Browse Source

Support unencrypted multiplayer games with no password

pull/3245/head
staphen 4 years ago committed by Anders Jenbo
parent
commit
ffbbcc6d62
  1. 45
      CMakeLists.txt
  2. 4
      Source/DiabloUI/diabloui.cpp
  3. 61
      Source/DiabloUI/selgame.cpp
  4. 2
      Source/DiabloUI/selhero.cpp
  5. 4
      Source/DiabloUI/ui_item.h
  6. 9
      Source/automap.cpp
  7. 8
      Source/dvlnet/abstract_net.h
  8. 5
      Source/dvlnet/base.cpp
  9. 5
      Source/dvlnet/base.h
  10. 14
      Source/dvlnet/base_protocol.h
  11. 27
      Source/dvlnet/cdwrap.h
  12. 4
      Source/dvlnet/loopback.cpp
  13. 4
      Source/dvlnet/loopback.h
  14. 198
      Source/dvlnet/packet.cpp
  15. 31
      Source/dvlnet/packet.h
  16. 13
      Source/dvlnet/tcp_client.cpp
  17. 4
      Source/dvlnet/tcp_client.h
  18. 4
      Source/dvlnet/tcp_server.cpp
  19. 4
      Source/dvlnet/tcp_server.h
  20. 2
      Source/multi.cpp
  21. 1
      Source/multi.h
  22. 2
      Source/storm/storm.h
  23. 27
      Source/storm/storm_net.cpp
  24. 5
      vcpkg.json

45
CMakeLists.txt

@ -27,15 +27,13 @@ option(BINARY_RELEASE "Enable options for binary release" OFF)
option(NIGHTLY_BUILD "Enable options for nightly build" OFF) option(NIGHTLY_BUILD "Enable options for nightly build" OFF)
option(USE_SDL1 "Use SDL1.2 instead of SDL2" OFF) option(USE_SDL1 "Use SDL1.2 instead of SDL2" OFF)
option(NONET "Disable network support" OFF) option(NONET "Disable network support" OFF)
cmake_dependent_option(DISABLE_TCP "Disable TCP multiplayer option" OFF "NOT NONET" ON)
cmake_dependent_option(DISABLE_ZERO_TIER "Disable ZeroTier multiplayer option" OFF "NOT NONET" ON)
cmake_dependent_option(PACKET_ENCRYPTION "Encrypt network packets" ON "NOT NONET" OFF)
option(NOSOUND "Disable sound support" OFF) option(NOSOUND "Disable sound support" OFF)
option(RUN_TESTS "Build and run tests" OFF) option(RUN_TESTS "Build and run tests" OFF)
option(ENABLE_CODECOVERAGE "Instrument code for code coverage (only enabled with RUN_TESTS)" OFF) option(ENABLE_CODECOVERAGE "Instrument code for code coverage (only enabled with RUN_TESTS)" OFF)
if(NOT NONET)
option(DISABLE_TCP "Disable TCP multiplayer option" OFF)
option(DISABLE_ZERO_TIER "Disable ZeroTier multiplayer option" OFF)
endif()
option(DISABLE_STREAMING_MUSIC "Disable streaming music (to work around broken platform implementations)" OFF) option(DISABLE_STREAMING_MUSIC "Disable streaming music (to work around broken platform implementations)" OFF)
mark_as_advanced(DISABLE_STREAMING_MUSIC) mark_as_advanced(DISABLE_STREAMING_MUSIC)
option(DISABLE_STREAMING_SOUNDS "Disable streaming sounds (to work around broken platform implementations)" OFF) option(DISABLE_STREAMING_SOUNDS "Disable streaming sounds (to work around broken platform implementations)" OFF)
@ -65,6 +63,9 @@ if(NIGHTLY_BUILD OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
set(CPACK ON) set(CPACK ON)
endif() endif()
if(PACKET_ENCRYPTION)
list(APPEND VCPKG_MANIFEST_FEATURES "encryption")
endif()
if(USE_GETTEXT_FROM_VCPKG) if(USE_GETTEXT_FROM_VCPKG)
list(APPEND VCPKG_MANIFEST_FEATURES "translations") list(APPEND VCPKG_MANIFEST_FEATURES "translations")
endif() endif()
@ -78,7 +79,7 @@ if(NOT NOSOUND)
"DEVILUTIONX_SYSTEM_SDL_AUDIOLIB AND NOT DIST" ON) "DEVILUTIONX_SYSTEM_SDL_AUDIOLIB AND NOT DIST" ON)
endif() endif()
if(NOT NONET) if(PACKET_ENCRYPTION)
option(DEVILUTIONX_SYSTEM_LIBSODIUM "Use system-provided libsodium" ON) option(DEVILUTIONX_SYSTEM_LIBSODIUM "Use system-provided libsodium" ON)
cmake_dependent_option(DEVILUTIONX_STATIC_LIBSODIUM "Link static libsodium" OFF cmake_dependent_option(DEVILUTIONX_STATIC_LIBSODIUM "Link static libsodium" OFF
"DEVILUTIONX_SYSTEM_LIBSODIUM AND NOT DIST" ON) "DEVILUTIONX_SYSTEM_LIBSODIUM AND NOT DIST" ON)
@ -216,6 +217,13 @@ if(PIE)
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
endif() endif()
if(NONET)
# Fix dependent options if platform defs disable network
set(DISABLE_TCP ON)
set(DISABLE_ZERO_TIER ON)
set(PACKET_ENCRYPTION OFF)
endif()
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@ -227,7 +235,7 @@ if(NOT NINTENDO_3DS)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
endif() endif()
if(NOT NONET) if(PACKET_ENCRYPTION)
if(DEVILUTIONX_SYSTEM_LIBSODIUM) if(DEVILUTIONX_SYSTEM_LIBSODIUM)
set(sodium_USE_STATIC_LIBS ${DEVILUTIONX_STATIC_LIBSODIUM}) set(sodium_USE_STATIC_LIBS ${DEVILUTIONX_STATIC_LIBSODIUM})
find_package(sodium REQUIRED) find_package(sodium REQUIRED)
@ -531,18 +539,26 @@ if(NINTENDO_SWITCH)
Source/platform/switch/network.cpp Source/platform/switch/network.cpp
Source/platform/switch/keyboard.cpp Source/platform/switch/keyboard.cpp
Source/platform/switch/docking.cpp Source/platform/switch/docking.cpp
Source/platform/switch/random.cpp
Source/platform/switch/asio/pause.c Source/platform/switch/asio/pause.c
Source/platform/switch/asio/net/if.c Source/platform/switch/asio/net/if.c
Source/platform/switch/asio/sys/signal.c) Source/platform/switch/asio/sys/signal.c)
if(PACKET_ENCRYPTION)
list(APPEND libdevilutionx_SRCS
Source/platform/switch/random.cpp)
endif()
endif() endif()
if(VITA) if(VITA)
list(APPEND libdevilutionx_SRCS list(APPEND libdevilutionx_SRCS
Source/platform/vita/random.cpp
Source/platform/vita/network.cpp Source/platform/vita/network.cpp
Source/platform/vita/keyboard.cpp Source/platform/vita/keyboard.cpp
Source/platform/vita/touch.cpp) Source/platform/vita/touch.cpp)
if(PACKET_ENCRYPTION)
list(APPEND libdevilutionx_SRCS
Source/platform/vita/random.cpp)
endif()
endif() endif()
if(NINTENDO_3DS) if(NINTENDO_3DS)
@ -551,13 +567,17 @@ if(NINTENDO_3DS)
Source/platform/ctr/keyboard.cpp Source/platform/ctr/keyboard.cpp
Source/platform/ctr/display.cpp Source/platform/ctr/display.cpp
Source/platform/ctr/messagebox.cpp Source/platform/ctr/messagebox.cpp
Source/platform/ctr/random.cpp
Source/platform/ctr/sockets.cpp Source/platform/ctr/sockets.cpp
Source/platform/ctr/locale.cpp Source/platform/ctr/locale.cpp
Source/platform/ctr/asio/net/if.c Source/platform/ctr/asio/net/if.c
Source/platform/ctr/asio/sys/socket.c Source/platform/ctr/asio/sys/socket.c
Source/platform/ctr/asio/sys/uio.c) Source/platform/ctr/asio/sys/uio.c)
set(BIN_TARGET ${BIN_TARGET}.elf) set(BIN_TARGET ${BIN_TARGET}.elf)
if(PACKET_ENCRYPTION)
list(APPEND libdevilutionx_SRCS
Source/platform/ctr/random.cpp)
endif()
endif() endif()
if(RUN_TESTS) if(RUN_TESTS)
@ -847,7 +867,9 @@ if(NOT NONET)
if(NOT DISABLE_TCP) if(NOT DISABLE_TCP)
target_link_libraries(libdevilutionx PUBLIC asio) target_link_libraries(libdevilutionx PUBLIC asio)
endif() endif()
target_link_libraries(libdevilutionx PUBLIC sodium) if(PACKET_ENCRYPTION)
target_link_libraries(libdevilutionx PUBLIC sodium)
endif()
endif() endif()
target_link_libraries(libdevilutionx PUBLIC fmt::fmt) target_link_libraries(libdevilutionx PUBLIC fmt::fmt)
@ -874,6 +896,7 @@ foreach(
GPERF_HEAP_MAIN GPERF_HEAP_MAIN
GPERF_HEAP_FIRST_GAME_ITERATION GPERF_HEAP_FIRST_GAME_ITERATION
STREAM_ALL_AUDIO STREAM_ALL_AUDIO
PACKET_ENCRYPTION
VIRTUAL_GAMEPAD VIRTUAL_GAMEPAD
) )
if(${def_name}) if(${def_name})

4
Source/DiabloUI/diabloui.cpp

@ -64,6 +64,7 @@ bool UiItemsWraps;
char *UiTextInput; char *UiTextInput;
int UiTextInputLen; int UiTextInputLen;
bool textInputActive = true; bool textInputActive = true;
bool allowEmptyTextInput = false;
std::size_t SelectedItem = 0; std::size_t SelectedItem = 0;
@ -111,6 +112,7 @@ void UiInitList(int count, void (*fnFocus)(int value), void (*fnSelect)(int valu
auto *pItemUIEdit = static_cast<UiEdit *>(item.get()); auto *pItemUIEdit = static_cast<UiEdit *>(item.get());
SDL_SetTextInputRect(&item->m_rect); SDL_SetTextInputRect(&item->m_rect);
textInputActive = true; textInputActive = true;
allowEmptyTextInput = pItemUIEdit->m_allowEmpty;
#ifdef __SWITCH__ #ifdef __SWITCH__
switch_start_text_input(pItemUIEdit->m_hint, pItemUIEdit->m_value, pItemUIEdit->m_max_length, /*multiline=*/0); switch_start_text_input(pItemUIEdit->m_hint, pItemUIEdit->m_value, pItemUIEdit->m_max_length, /*multiline=*/0);
#elif defined(__vita__) #elif defined(__vita__)
@ -425,7 +427,7 @@ void UiFocusNavigationSelect()
{ {
UiPlaySelectSound(); UiPlaySelectSound();
if (textInputActive) { if (textInputActive) {
if (strlen(UiTextInput) == 0) { if (!allowEmptyTextInput && strlen(UiTextInput) == 0) {
return; return;
} }
#ifndef __SWITCH__ #ifndef __SWITCH__

61
Source/DiabloUI/selgame.cpp

@ -41,8 +41,6 @@ std::vector<std::unique_ptr<UiItemBase>> vecSelGameDialog;
std::vector<std::string> Gamelist; std::vector<std::string> Gamelist;
int HighlightedItem; int HighlightedItem;
constexpr const char *DefaultPassword = "asd";
} // namespace } // namespace
void selgame_FreeVectors() void selgame_FreeVectors()
@ -89,11 +87,14 @@ void selgame_GameSelection_Init()
SDL_Rect rect4 = { (Sint16)(PANEL_LEFT + 300), (Sint16)(UI_OFFSET_Y + 211), 295, 33 }; SDL_Rect rect4 = { (Sint16)(PANEL_LEFT + 300), (Sint16)(UI_OFFSET_Y + 211), 295, 33 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Select Action"), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3)); vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Select Action"), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
#ifdef PACKET_ENCRYPTION
vecSelGameDlgItems.push_back(std::make_unique<UiListItem>(_("Create Game"), 0)); vecSelGameDlgItems.push_back(std::make_unique<UiListItem>(_("Create Game"), 0));
vecSelGameDlgItems.push_back(std::make_unique<UiListItem>(_("Join Game"), 1)); #endif
vecSelGameDlgItems.push_back(std::make_unique<UiListItem>(_("Create Public Game"), 1));
vecSelGameDlgItems.push_back(std::make_unique<UiListItem>(_("Join Game"), 2));
for (unsigned i = 0; i < Gamelist.size(); i++) { for (unsigned i = 0; i < Gamelist.size(); i++) {
vecSelGameDlgItems.push_back(std::make_unique<UiListItem>(Gamelist[i].c_str(), i + 2)); vecSelGameDlgItems.push_back(std::make_unique<UiListItem>(Gamelist[i].c_str(), i + 3));
} }
vecSelGameDialog.push_back(std::make_unique<UiList>(vecSelGameDlgItems, PANEL_LEFT + 305, (UI_OFFSET_Y + 255), 285, 26, UiFlags::AlignCenter | UiFlags::FontSize24 | UiFlags::ColorUiGold)); vecSelGameDialog.push_back(std::make_unique<UiList>(vecSelGameDlgItems, PANEL_LEFT + 305, (UI_OFFSET_Y + 255), 285, 26, UiFlags::AlignCenter | UiFlags::FontSize24 | UiFlags::ColorUiGold));
@ -104,7 +105,13 @@ void selgame_GameSelection_Init()
SDL_Rect rect6 = { (Sint16)(PANEL_LEFT + 449), (Sint16)(UI_OFFSET_Y + 427), 140, 35 }; SDL_Rect rect6 = { (Sint16)(PANEL_LEFT + 449), (Sint16)(UI_OFFSET_Y + 427), 140, 35 };
vecSelGameDialog.push_back(std::make_unique<UiArtTextButton>(_("CANCEL"), &UiFocusNavigationEsc, rect6, UiFlags::AlignCenter | UiFlags::VerticalCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold)); vecSelGameDialog.push_back(std::make_unique<UiArtTextButton>(_("CANCEL"), &UiFocusNavigationEsc, rect6, UiFlags::AlignCenter | UiFlags::VerticalCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
UiInitList(vecSelGameDlgItems.size(), selgame_GameSelection_Focus, selgame_GameSelection_Select, selgame_GameSelection_Esc, vecSelGameDialog, true, nullptr, HighlightedItem); auto selectFn = [](int index) {
// UiListItem::m_value could be different from
// the index if packet encryption is disabled
int itemValue = vecSelGameDlgItems[index]->m_value;
selgame_GameSelection_Select(itemValue);
};
UiInitList(vecSelGameDlgItems.size(), selgame_GameSelection_Focus, selectFn, selgame_GameSelection_Esc, vecSelGameDialog, true, nullptr, HighlightedItem);
} }
void selgame_GameSelection_Focus(int value) void selgame_GameSelection_Focus(int value)
@ -115,6 +122,9 @@ void selgame_GameSelection_Focus(int value)
strncpy(selgame_Description, _("Create a new game with a difficulty setting of your choice."), sizeof(selgame_Description) - 1); strncpy(selgame_Description, _("Create a new game with a difficulty setting of your choice."), sizeof(selgame_Description) - 1);
break; break;
case 1: case 1:
strncpy(selgame_Description, _("Create a new public game that anyone can join with a difficulty setting of your choice."), sizeof(selgame_Description) - 1);
break;
case 2:
strncpy(selgame_Description, _("Enter an IP or a hostname and join a game already in progress at that address."), sizeof(selgame_Description) - 1); strncpy(selgame_Description, _("Enter an IP or a hostname and join a game already in progress at that address."), sizeof(selgame_Description) - 1);
break; break;
default: default:
@ -143,9 +153,8 @@ void selgame_GameSelection_Select(int value)
selgame_enteringGame = true; selgame_enteringGame = true;
selgame_selectedGame = value; selgame_selectedGame = value;
if (value > 1 && selgame_selectedGame != 0) { if (value > 2) {
strcpy(selgame_Ip, Gamelist[value - 2].c_str()); strcpy(selgame_Ip, Gamelist[value - 3].c_str());
strcpy(selgame_Password, DefaultPassword);
selgame_Password_Select(value); selgame_Password_Select(value);
return; return;
} }
@ -167,7 +176,8 @@ void selgame_GameSelection_Select(int value)
vecSelGameDialog.push_back(std::make_unique<UiArtText>(selgame_Description, rect3, UiFlags::FontSize12 | UiFlags::ColorUiSilverDark, 1, 16)); vecSelGameDialog.push_back(std::make_unique<UiArtText>(selgame_Description, rect3, UiFlags::FontSize12 | UiFlags::ColorUiSilverDark, 1, 16));
switch (value) { switch (value) {
case 0: { case 0:
case 1: {
title = _("Create Game"); title = _("Create Game");
SDL_Rect rect4 = { (Sint16)(PANEL_LEFT + 299), (Sint16)(UI_OFFSET_Y + 211), 295, 35 }; SDL_Rect rect4 = { (Sint16)(PANEL_LEFT + 299), (Sint16)(UI_OFFSET_Y + 211), 295, 35 };
@ -188,14 +198,14 @@ void selgame_GameSelection_Select(int value)
UiInitList(vecSelGameDlgItems.size(), selgame_Diff_Focus, selgame_Diff_Select, selgame_Diff_Esc, vecSelGameDialog, true); UiInitList(vecSelGameDlgItems.size(), selgame_Diff_Focus, selgame_Diff_Select, selgame_Diff_Esc, vecSelGameDialog, true);
break; break;
} }
case 1: { case 2: {
title = _("Join TCP Games"); title = _("Join TCP Games");
SDL_Rect rect4 = { (Sint16)(PANEL_LEFT + 305), (Sint16)(UI_OFFSET_Y + 211), 285, 33 }; SDL_Rect rect4 = { (Sint16)(PANEL_LEFT + 305), (Sint16)(UI_OFFSET_Y + 211), 285, 33 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Enter address"), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3)); vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Enter address"), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
SDL_Rect rect5 = { (Sint16)(PANEL_LEFT + 305), (Sint16)(UI_OFFSET_Y + 314), 285, 33 }; SDL_Rect rect5 = { (Sint16)(PANEL_LEFT + 305), (Sint16)(UI_OFFSET_Y + 314), 285, 33 };
vecSelGameDialog.push_back(std::make_unique<UiEdit>(_("Enter address"), selgame_Ip, 128, rect5, UiFlags::FontSize24 | UiFlags::ColorUiGold)); vecSelGameDialog.push_back(std::make_unique<UiEdit>(_("Enter address"), selgame_Ip, 128, false, rect5, UiFlags::FontSize24 | UiFlags::ColorUiGold));
SDL_Rect rect6 = { (Sint16)(PANEL_LEFT + 299), (Sint16)(UI_OFFSET_Y + 427), 140, 35 }; SDL_Rect rect6 = { (Sint16)(PANEL_LEFT + 299), (Sint16)(UI_OFFSET_Y + 427), 140, 35 };
vecSelGameDialog.push_back(std::make_unique<UiArtTextButton>(_("OK"), &UiFocusNavigationSelect, rect6, UiFlags::AlignCenter | UiFlags::VerticalCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold)); vecSelGameDialog.push_back(std::make_unique<UiArtTextButton>(_("OK"), &UiFocusNavigationSelect, rect6, UiFlags::AlignCenter | UiFlags::VerticalCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
@ -204,7 +214,12 @@ void selgame_GameSelection_Select(int value)
vecSelGameDialog.push_back(std::make_unique<UiArtTextButton>(_("CANCEL"), &UiFocusNavigationEsc, rect7, UiFlags::AlignCenter | UiFlags::VerticalCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold)); vecSelGameDialog.push_back(std::make_unique<UiArtTextButton>(_("CANCEL"), &UiFocusNavigationEsc, rect7, UiFlags::AlignCenter | UiFlags::VerticalCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
HighlightedItem = 0; HighlightedItem = 0;
#ifdef PACKET_ENCRYPTION
UiInitList(0, nullptr, selgame_Password_Init, selgame_GameSelection_Init, vecSelGameDialog); UiInitList(0, nullptr, selgame_Password_Init, selgame_GameSelection_Init, vecSelGameDialog);
#else
UiInitList(0, nullptr, selgame_Password_Select, selgame_GameSelection_Init, vecSelGameDialog);
#endif
break; break;
} }
} }
@ -375,7 +390,7 @@ void selgame_Speed_Select(int value)
{ {
nTickRate = vecSelGameDlgItems[value]->m_value; nTickRate = vecSelGameDlgItems[value]->m_value;
if (provider == SELCONN_LOOPBACK) { if (provider == SELCONN_LOOPBACK || selgame_selectedGame == 1) {
selgame_Password_Select(0); selgame_Password_Select(0);
return; return;
} }
@ -404,8 +419,10 @@ void selgame_Password_Init(int /*value*/)
SDL_Rect rect4 = { (Sint16)(PANEL_LEFT + 305), (Sint16)(UI_OFFSET_Y + 211), 285, 33 }; SDL_Rect rect4 = { (Sint16)(PANEL_LEFT + 305), (Sint16)(UI_OFFSET_Y + 211), 285, 33 };
vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Enter Password"), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3)); vecSelGameDialog.push_back(std::make_unique<UiArtText>(_("Enter Password"), rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
// Allow password to be empty only when joining games
bool allowEmpty = selgame_selectedGame == 2;
SDL_Rect rect5 = { (Sint16)(PANEL_LEFT + 305), (Sint16)(UI_OFFSET_Y + 314), 285, 33 }; SDL_Rect rect5 = { (Sint16)(PANEL_LEFT + 305), (Sint16)(UI_OFFSET_Y + 314), 285, 33 };
vecSelGameDialog.push_back(std::make_unique<UiEdit>(_("Enter Password"), selgame_Password, 15, rect5, UiFlags::FontSize24 | UiFlags::ColorUiGold)); vecSelGameDialog.push_back(std::make_unique<UiEdit>(_("Enter Password"), selgame_Password, 15, allowEmpty, rect5, UiFlags::FontSize24 | UiFlags::ColorUiGold));
SDL_Rect rect6 = { (Sint16)(PANEL_LEFT + 299), (Sint16)(UI_OFFSET_Y + 427), 140, 35 }; SDL_Rect rect6 = { (Sint16)(PANEL_LEFT + 299), (Sint16)(UI_OFFSET_Y + 427), 140, 35 };
vecSelGameDialog.push_back(std::make_unique<UiArtTextButton>(_("OK"), &UiFocusNavigationSelect, rect6, UiFlags::AlignCenter | UiFlags::VerticalCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold)); vecSelGameDialog.push_back(std::make_unique<UiArtTextButton>(_("OK"), &UiFocusNavigationSelect, rect6, UiFlags::AlignCenter | UiFlags::VerticalCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
@ -443,9 +460,15 @@ static bool IsGameCompatible(const GameData &data)
void selgame_Password_Select(int /*value*/) void selgame_Password_Select(int /*value*/)
{ {
if (selgame_selectedGame != 0) { char *gamePassword = nullptr;
if (selgame_selectedGame == 0)
gamePassword = selgame_Password;
if (selgame_selectedGame == 2 && strlen(selgame_Password) > 0)
gamePassword = selgame_Password;
if (selgame_selectedGame > 1) {
strcpy(sgOptions.Network.szPreviousHost, selgame_Ip); strcpy(sgOptions.Network.szPreviousHost, selgame_Ip);
if (SNetJoinGame(selgame_Ip, selgame_Password, gdwPlayerId)) { if (SNetJoinGame(selgame_Ip, gamePassword, gdwPlayerId)) {
if (!IsGameCompatible(*m_game_data)) { if (!IsGameCompatible(*m_game_data)) {
selgame_GameSelection_Select(1); selgame_GameSelection_Select(1);
return; return;
@ -468,7 +491,7 @@ void selgame_Password_Select(int /*value*/)
m_game_data->bTheoQuest = sgOptions.Gameplay.bTheoQuest ? 1 : 0; m_game_data->bTheoQuest = sgOptions.Gameplay.bTheoQuest ? 1 : 0;
m_game_data->bCowQuest = sgOptions.Gameplay.bCowQuest ? 1 : 0; m_game_data->bCowQuest = sgOptions.Gameplay.bCowQuest ? 1 : 0;
if (SNetCreateGame(nullptr, selgame_Password, (char *)m_game_data, sizeof(*m_game_data), gdwPlayerId)) { if (SNetCreateGame(nullptr, gamePassword, (char *)m_game_data, sizeof(*m_game_data), gdwPlayerId)) {
UiInitList_clear(); UiInitList_clear();
selgame_endMenu = true; selgame_endMenu = true;
} else { } else {
@ -481,8 +504,8 @@ void selgame_Password_Select(int /*value*/)
void selgame_Password_Esc() void selgame_Password_Esc()
{ {
if (selgame_selectedGame == 1) if (selgame_selectedGame == 2)
selgame_GameSelection_Select(1); selgame_GameSelection_Select(2);
else else
selgame_GameSpeedSelection(); selgame_GameSpeedSelection();
} }
@ -524,7 +547,7 @@ bool UiSelectGame(GameData *gameData, int *playerId)
selgame_endMenu = false; selgame_endMenu = false;
DvlNet_SetPassword(DefaultPassword); DvlNet_ClearPassword();
DvlNet_ClearGamelist(); DvlNet_ClearGamelist();
while (!selgame_endMenu) { while (!selgame_endMenu) {

2
Source/DiabloUI/selhero.cpp

@ -275,7 +275,7 @@ void SelheroClassSelectorSelect(int value)
vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Enter Name"), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3)); vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Enter Name"), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
SDL_Rect rect2 = { (Sint16)(PANEL_LEFT + 265), (Sint16)(UI_OFFSET_Y + 317), 320, 33 }; SDL_Rect rect2 = { (Sint16)(PANEL_LEFT + 265), (Sint16)(UI_OFFSET_Y + 317), 320, 33 };
vecSelDlgItems.push_back(std::make_unique<UiEdit>(_("Enter Name"), selhero_heroInfo.name, 15, rect2, UiFlags::FontSize24 | UiFlags::ColorUiGold)); vecSelDlgItems.push_back(std::make_unique<UiEdit>(_("Enter Name"), selhero_heroInfo.name, 15, false, rect2, UiFlags::FontSize24 | UiFlags::ColorUiGold));
SDL_Rect rect3 = { (Sint16)(PANEL_LEFT + 279), (Sint16)(UI_OFFSET_Y + 429), 140, 35 }; SDL_Rect rect3 = { (Sint16)(PANEL_LEFT + 279), (Sint16)(UI_OFFSET_Y + 429), 140, 35 };
vecSelDlgItems.push_back(std::make_unique<UiArtTextButton>(_("OK"), &UiFocusNavigationSelect, rect3, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold)); vecSelDlgItems.push_back(std::make_unique<UiArtTextButton>(_("OK"), &UiFocusNavigationSelect, rect3, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));

4
Source/DiabloUI/ui_item.h

@ -224,11 +224,12 @@ public:
class UiEdit : public UiItemBase { class UiEdit : public UiItemBase {
public: public:
UiEdit(const char *hint, char *value, std::size_t max_length, SDL_Rect rect, UiFlags flags = UiFlags::None) UiEdit(const char *hint, char *value, std::size_t max_length, bool allowEmpty, SDL_Rect rect, UiFlags flags = UiFlags::None)
: UiItemBase(UiType::Edit, rect, flags) : UiItemBase(UiType::Edit, rect, flags)
, m_hint(hint) , m_hint(hint)
, m_value(value) , m_value(value)
, m_max_length(max_length) , m_max_length(max_length)
, m_allowEmpty(allowEmpty)
{ {
} }
@ -236,6 +237,7 @@ public:
const char *m_hint; const char *m_hint;
char *m_value; char *m_value;
std::size_t m_max_length; std::size_t m_max_length;
bool m_allowEmpty;
}; };
//============================================================================= //=============================================================================

9
Source/automap.cpp

@ -466,11 +466,12 @@ void DrawAutomapText(const Surface &out)
linePosition.y += 15; linePosition.y += 15;
} }
if (szPlayerDescript[0] != '\0') { if (!PublicGame)
strcat(strcpy(desc, _("password: ")), szPlayerDescript); strcat(strcpy(desc, _("password: ")), szPlayerDescript);
DrawString(out, desc, linePosition); else
linePosition.y += 15; strcpy(desc, _("Public Game"));
} DrawString(out, desc, linePosition);
linePosition.y += 15;
} }
if (setlevel) { if (setlevel) {

8
Source/dvlnet/abstract_net.h

@ -22,8 +22,8 @@ public:
class abstract_net { class abstract_net {
public: public:
virtual int create(std::string addrstr, std::string passwd) = 0; virtual int create(std::string addrstr) = 0;
virtual int join(std::string addrstr, std::string passwd) = 0; virtual int join(std::string addrstr) = 0;
virtual bool SNetReceiveMessage(int *sender, void **data, uint32_t *size) = 0; virtual bool SNetReceiveMessage(int *sender, void **data, uint32_t *size) = 0;
virtual bool SNetSendMessage(int dest, void *data, unsigned int size) = 0; virtual bool SNetSendMessage(int dest, void *data, unsigned int size) = 0;
virtual bool SNetReceiveTurns(char **data, size_t *size, uint32_t *status) = 0; virtual bool SNetReceiveTurns(char **data, size_t *size, uint32_t *status) = 0;
@ -44,6 +44,10 @@ public:
{ {
} }
virtual void clear_password()
{
}
virtual void send_info_request() virtual void send_info_request()
{ {
} }

5
Source/dvlnet/base.cpp

@ -17,6 +17,11 @@ void base::setup_password(std::string pw)
pktfty = std::make_unique<packet_factory>(pw); pktfty = std::make_unique<packet_factory>(pw);
} }
void base::clear_password()
{
pktfty = std::make_unique<packet_factory>();
}
void base::RunEventHandler(_SNETEVENT &ev) void base::RunEventHandler(_SNETEVENT &ev)
{ {
auto f = registered_handlers[static_cast<event_type>(ev.eventid)]; auto f = registered_handlers[static_cast<event_type>(ev.eventid)];

5
Source/dvlnet/base.h

@ -14,8 +14,8 @@ namespace net {
class base : public abstract_net { class base : public abstract_net {
public: public:
virtual int create(std::string addrstr, std::string passwd) = 0; virtual int create(std::string addrstr) = 0;
virtual int join(std::string addrstr, std::string passwd) = 0; virtual int join(std::string addrstr) = 0;
virtual bool SNetReceiveMessage(int *sender, void **data, uint32_t *size); virtual bool SNetReceiveMessage(int *sender, void **data, uint32_t *size);
virtual bool SNetSendMessage(int playerId, void *data, unsigned int size); virtual bool SNetSendMessage(int playerId, void *data, unsigned int size);
@ -37,6 +37,7 @@ public:
void setup_gameinfo(buffer_t info); void setup_gameinfo(buffer_t info);
virtual void setup_password(std::string pw); virtual void setup_password(std::string pw);
virtual void clear_password();
virtual ~base() = default; virtual ~base() = default;

14
Source/dvlnet/base_protocol.h

@ -16,8 +16,8 @@ namespace net {
template <class P> template <class P>
class base_protocol : public base { class base_protocol : public base {
public: public:
virtual int create(std::string addrstr, std::string passwd); virtual int create(std::string addrstr);
virtual int join(std::string addrstr, std::string passwd); virtual int join(std::string addrstr);
virtual void poll(); virtual void poll();
virtual void send(packet &pkt); virtual void send(packet &pkt);
virtual void DisconnectNet(plr_t plr); virtual void DisconnectNet(plr_t plr);
@ -108,8 +108,7 @@ void base_protocol<P>::send_info_request()
template <class P> template <class P>
void base_protocol<P>::wait_join() void base_protocol<P>::wait_join()
{ {
randombytes_buf(reinterpret_cast<unsigned char *>(&cookie_self), cookie_self = packet_out::GenerateCookie();
sizeof(cookie_t));
auto pkt = pktfty->make_packet<PT_JOIN_REQUEST>(PLR_BROADCAST, auto pkt = pktfty->make_packet<PT_JOIN_REQUEST>(PLR_BROADCAST,
PLR_MASTER, cookie_self, game_init_info); PLR_MASTER, cookie_self, game_init_info);
proto.send(firstpeer, pkt->Data()); proto.send(firstpeer, pkt->Data());
@ -122,9 +121,8 @@ void base_protocol<P>::wait_join()
} }
template <class P> template <class P>
int base_protocol<P>::create(std::string addrstr, std::string passwd) int base_protocol<P>::create(std::string addrstr)
{ {
setup_password(passwd);
gamename = addrstr; gamename = addrstr;
if (wait_network()) { if (wait_network()) {
@ -136,10 +134,8 @@ int base_protocol<P>::create(std::string addrstr, std::string passwd)
} }
template <class P> template <class P>
int base_protocol<P>::join(std::string addrstr, std::string passwd) int base_protocol<P>::join(std::string addrstr)
{ {
//addrstr = "fd80:56c2:e21c:0:199:931d:b14:c4d2";
setup_password(passwd);
gamename = addrstr; gamename = addrstr;
if (wait_network()) if (wait_network())
if (wait_firstpeer()) if (wait_firstpeer())

27
Source/dvlnet/cdwrap.h

@ -17,12 +17,13 @@ private:
std::unique_ptr<abstract_net> dvlnet_wrap; std::unique_ptr<abstract_net> dvlnet_wrap;
std::map<event_type, SEVTHANDLER> registered_handlers; std::map<event_type, SEVTHANDLER> registered_handlers;
buffer_t game_init_info; buffer_t game_init_info;
std::optional<std::string> game_pw;
void reset(); void reset();
public: public:
virtual int create(std::string addrstr, std::string passwd); virtual int create(std::string addrstr);
virtual int join(std::string addrstr, std::string passwd); virtual int join(std::string addrstr);
virtual bool SNetReceiveMessage(int *sender, void **data, uint32_t *size); virtual bool SNetReceiveMessage(int *sender, void **data, uint32_t *size);
virtual bool SNetSendMessage(int dest, void *data, unsigned int size); virtual bool SNetSendMessage(int dest, void *data, unsigned int size);
virtual bool SNetReceiveTurns(char **data, size_t *size, uint32_t *status); virtual bool SNetReceiveTurns(char **data, size_t *size, uint32_t *status);
@ -41,6 +42,7 @@ public:
virtual void clear_gamelist(); virtual void clear_gamelist();
virtual std::vector<std::string> get_gamelist(); virtual std::vector<std::string> get_gamelist();
virtual void setup_password(std::string pw); virtual void setup_password(std::string pw);
virtual void clear_password();
cdwrap(); cdwrap();
virtual ~cdwrap() = default; virtual ~cdwrap() = default;
@ -58,23 +60,28 @@ void cdwrap<T>::reset()
dvlnet_wrap.reset(new T); dvlnet_wrap.reset(new T);
dvlnet_wrap->setup_gameinfo(game_init_info); dvlnet_wrap->setup_gameinfo(game_init_info);
if (game_pw != std::nullopt)
dvlnet_wrap->setup_password(*game_pw);
else
dvlnet_wrap->clear_password();
for (const auto &pair : registered_handlers) for (const auto &pair : registered_handlers)
dvlnet_wrap->SNetRegisterEventHandler(pair.first, pair.second); dvlnet_wrap->SNetRegisterEventHandler(pair.first, pair.second);
} }
template <class T> template <class T>
int cdwrap<T>::create(std::string addrstr, std::string passwd) int cdwrap<T>::create(std::string addrstr)
{ {
reset(); reset();
return dvlnet_wrap->create(addrstr, passwd); return dvlnet_wrap->create(addrstr);
} }
template <class T> template <class T>
int cdwrap<T>::join(std::string addrstr, std::string passwd) int cdwrap<T>::join(std::string addrstr)
{ {
game_init_info = buffer_t(); game_init_info = buffer_t();
reset(); reset();
return dvlnet_wrap->join(addrstr, passwd); return dvlnet_wrap->join(addrstr);
} }
template <class T> template <class T>
@ -186,8 +193,16 @@ std::vector<std::string> cdwrap<T>::get_gamelist()
template <class T> template <class T>
void cdwrap<T>::setup_password(std::string pw) void cdwrap<T>::setup_password(std::string pw)
{ {
game_pw = pw;
return dvlnet_wrap->setup_password(pw); return dvlnet_wrap->setup_password(pw);
} }
template <class T>
void cdwrap<T>::clear_password()
{
game_pw = std::nullopt;
return dvlnet_wrap->clear_password();
}
} // namespace net } // namespace net
} // namespace devilution } // namespace devilution

4
Source/dvlnet/loopback.cpp

@ -5,12 +5,12 @@
namespace devilution { namespace devilution {
namespace net { namespace net {
int loopback::create(std::string /*addrstr*/, std::string /*passwd*/) int loopback::create(std::string /*addrstr*/)
{ {
return plr_single; return plr_single;
} }
int loopback::join(std::string /*addrstr*/, std::string /*passwd*/) int loopback::join(std::string /*addrstr*/)
{ {
ABORT(); ABORT();
} }

4
Source/dvlnet/loopback.h

@ -20,8 +20,8 @@ public:
plr_single = 0; plr_single = 0;
}; };
virtual int create(std::string addrstr, std::string passwd); virtual int create(std::string addrstr);
virtual int join(std::string addrstr, std::string passwd); virtual int join(std::string addrstr);
virtual bool SNetReceiveMessage(int *sender, void **data, uint32_t *size); virtual bool SNetReceiveMessage(int *sender, void **data, uint32_t *size);
virtual bool SNetSendMessage(int dest, void *data, unsigned int size); virtual bool SNetSendMessage(int dest, void *data, unsigned int size);
virtual bool SNetReceiveTurns(char **data, size_t *size, uint32_t *status); virtual bool SNetReceiveTurns(char **data, size_t *size, uint32_t *status);

198
Source/dvlnet/packet.cpp

@ -1,10 +1,52 @@
#ifdef PACKET_ENCRYPTION
#include <sodium.h>
#else
#include <chrono>
#include <random>
#endif
#include "dvlnet/packet.h" #include "dvlnet/packet.h"
namespace devilution { namespace devilution {
namespace net { namespace net {
#ifndef NONET #ifdef PACKET_ENCRYPTION
static constexpr bool DisableEncryption = false;
cookie_t packet_out::GenerateCookie()
{
cookie_t cookie;
randombytes_buf(reinterpret_cast<unsigned char *>(&cookie),
sizeof(cookie_t));
return cookie;
}
#else
class cookie_generator {
public:
cookie_generator()
{
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
generator.seed(seed);
}
cookie_t NewCookie()
{
return distribution(generator);
}
private:
std::default_random_engine generator;
std::uniform_int_distribution<cookie_t> distribution;
};
cookie_generator CookieGenerator;
cookie_t packet_out::GenerateCookie()
{
return CookieGenerator.NewCookie();
}
#endif #endif
const char *packet_type_to_string(uint8_t packetType) const char *packet_type_to_string(uint8_t packetType)
@ -66,158 +108,153 @@ void CheckPacketTypeOneOf(std::initializer_list<packet_type> expectedTypes, std:
const buffer_t &packet::Data() const buffer_t &packet::Data()
{ {
if (!have_decrypted || !have_encrypted) assert(have_encrypted || have_decrypted);
ABORT(); if (have_encrypted)
return encrypted_buffer; return encrypted_buffer;
return decrypted_buffer;
} }
packet_type packet::Type() packet_type packet::Type()
{ {
if (!have_decrypted) assert(have_decrypted);
ABORT();
return m_type; return m_type;
} }
plr_t packet::Source() const plr_t packet::Source() const
{ {
if (!have_decrypted) assert(have_decrypted);
ABORT();
return m_src; return m_src;
} }
plr_t packet::Destination() const plr_t packet::Destination() const
{ {
if (!have_decrypted) assert(have_decrypted);
ABORT();
return m_dest; return m_dest;
} }
const buffer_t &packet::Message() const buffer_t &packet::Message()
{ {
if (!have_decrypted) assert(have_decrypted);
ABORT();
CheckPacketTypeOneOf({ PT_MESSAGE }, m_type); CheckPacketTypeOneOf({ PT_MESSAGE }, m_type);
return m_message; return m_message;
} }
turn_t packet::Turn() turn_t packet::Turn()
{ {
if (!have_decrypted) assert(have_decrypted);
ABORT();
CheckPacketTypeOneOf({ PT_TURN }, m_type); CheckPacketTypeOneOf({ PT_TURN }, m_type);
return m_turn; return m_turn;
} }
cookie_t packet::Cookie() cookie_t packet::Cookie()
{ {
if (!have_decrypted) assert(have_decrypted);
ABORT();
CheckPacketTypeOneOf({ PT_JOIN_REQUEST, PT_JOIN_ACCEPT }, m_type); CheckPacketTypeOneOf({ PT_JOIN_REQUEST, PT_JOIN_ACCEPT }, m_type);
return m_cookie; return m_cookie;
} }
plr_t packet::NewPlayer() plr_t packet::NewPlayer()
{ {
if (!have_decrypted) assert(have_decrypted);
ABORT();
CheckPacketTypeOneOf({ PT_JOIN_ACCEPT, PT_CONNECT, PT_DISCONNECT }, m_type); CheckPacketTypeOneOf({ PT_JOIN_ACCEPT, PT_CONNECT, PT_DISCONNECT }, m_type);
return m_newplr; return m_newplr;
} }
const buffer_t &packet::Info() const buffer_t &packet::Info()
{ {
if (!have_decrypted) assert(have_decrypted);
ABORT();
CheckPacketTypeOneOf({ PT_JOIN_REQUEST, PT_JOIN_ACCEPT, PT_CONNECT, PT_INFO_REPLY }, m_type); CheckPacketTypeOneOf({ PT_JOIN_REQUEST, PT_JOIN_ACCEPT, PT_CONNECT, PT_INFO_REPLY }, m_type);
return m_info; return m_info;
} }
leaveinfo_t packet::LeaveInfo() leaveinfo_t packet::LeaveInfo()
{ {
if (!have_decrypted) assert(have_decrypted);
ABORT();
CheckPacketTypeOneOf({ PT_DISCONNECT }, m_type); CheckPacketTypeOneOf({ PT_DISCONNECT }, m_type);
return m_leaveinfo; return m_leaveinfo;
} }
void packet_in::Create(buffer_t buf) void packet_in::Create(buffer_t buf)
{ {
if (have_encrypted || have_decrypted) assert(!have_encrypted && !have_decrypted);
ABORT(); if (buf.size() < sizeof(packet_type) + 2 * sizeof(plr_t))
encrypted_buffer = std::move(buf); throw packet_exception();
decrypted_buffer = std::move(buf);
have_decrypted = true;
// TCP server implementation forwards the original data to clients
// so although we are not decrypting anything,
// we save a copy in encrypted_buffer anyway
encrypted_buffer = decrypted_buffer;
have_encrypted = true; have_encrypted = true;
} }
void packet_in::Decrypt() #ifdef PACKET_ENCRYPTION
void packet_in::Decrypt(buffer_t buf)
{ {
if (!have_encrypted) assert(!have_encrypted && !have_decrypted);
ABORT(); encrypted_buffer = std::move(buf);
if (have_decrypted) have_encrypted = true;
return;
#ifndef NONET
if (!DisableEncryption) {
if (encrypted_buffer.size() < crypto_secretbox_NONCEBYTES
+ crypto_secretbox_MACBYTES
+ sizeof(packet_type) + 2 * sizeof(plr_t))
throw packet_exception();
auto pktlen = (encrypted_buffer.size()
- crypto_secretbox_NONCEBYTES
- crypto_secretbox_MACBYTES);
decrypted_buffer.resize(pktlen);
int status = crypto_secretbox_open_easy(
decrypted_buffer.data(),
encrypted_buffer.data() + crypto_secretbox_NONCEBYTES,
encrypted_buffer.size() - crypto_secretbox_NONCEBYTES,
encrypted_buffer.data(),
key.data());
if (status != 0)
throw packet_exception();
} else
#endif
{
if (encrypted_buffer.size() < sizeof(packet_type) + 2 * sizeof(plr_t))
throw packet_exception();
decrypted_buffer = encrypted_buffer;
}
process_data(); if (encrypted_buffer.size() < crypto_secretbox_NONCEBYTES
+ crypto_secretbox_MACBYTES
+ sizeof(packet_type) + 2 * sizeof(plr_t))
throw packet_exception();
auto pktlen = (encrypted_buffer.size()
- crypto_secretbox_NONCEBYTES
- crypto_secretbox_MACBYTES);
decrypted_buffer.resize(pktlen);
int status = crypto_secretbox_open_easy(
decrypted_buffer.data(),
encrypted_buffer.data() + crypto_secretbox_NONCEBYTES,
encrypted_buffer.size() - crypto_secretbox_NONCEBYTES,
encrypted_buffer.data(),
key.data());
if (status != 0)
throw packet_exception();
have_decrypted = true; have_decrypted = true;
} }
#endif
#ifdef PACKET_ENCRYPTION
void packet_out::Encrypt() void packet_out::Encrypt()
{ {
if (!have_decrypted) assert(have_decrypted);
ABORT();
if (have_encrypted) if (have_encrypted)
return; return;
process_data(); auto lenCleartext = decrypted_buffer.size();
encrypted_buffer.insert(encrypted_buffer.begin(),
#ifndef NONET crypto_secretbox_NONCEBYTES, 0);
if (!DisableEncryption) { encrypted_buffer.insert(encrypted_buffer.end(),
auto lenCleartext = encrypted_buffer.size(); crypto_secretbox_MACBYTES + lenCleartext, 0);
encrypted_buffer.insert(encrypted_buffer.begin(), randombytes_buf(encrypted_buffer.data(), crypto_secretbox_NONCEBYTES);
crypto_secretbox_NONCEBYTES, 0); int status = crypto_secretbox_easy(
encrypted_buffer.insert(encrypted_buffer.end(), encrypted_buffer.data() + crypto_secretbox_NONCEBYTES,
crypto_secretbox_MACBYTES, 0); decrypted_buffer.data(),
randombytes_buf(encrypted_buffer.data(), crypto_secretbox_NONCEBYTES); lenCleartext,
int status = crypto_secretbox_easy( encrypted_buffer.data(),
encrypted_buffer.data() + crypto_secretbox_NONCEBYTES, key.data());
encrypted_buffer.data() + crypto_secretbox_NONCEBYTES, if (status != 0)
lenCleartext, ABORT();
encrypted_buffer.data(),
key.data());
if (status != 0)
ABORT();
}
#endif
have_encrypted = true; have_encrypted = true;
} }
#endif
packet_factory::packet_factory()
{
secure = false;
}
packet_factory::packet_factory(std::string pw) packet_factory::packet_factory(std::string pw)
{ {
#ifndef NONET secure = false;
#ifdef PACKET_ENCRYPTION
if (sodium_init() < 0) if (sodium_init() < 0)
ABORT(); ABORT();
pw.resize(std::min<std::size_t>(pw.size(), crypto_pwhash_argon2id_PASSWD_MAX)); pw.resize(std::min<std::size_t>(pw.size(), crypto_pwhash_argon2id_PASSWD_MAX));
@ -235,6 +272,7 @@ packet_factory::packet_factory(std::string pw)
crypto_pwhash_ALG_ARGON2ID13); crypto_pwhash_ALG_ARGON2ID13);
if (status != 0) if (status != 0)
ABORT(); ABORT();
secure = true;
#endif #endif
} }

31
Source/dvlnet/packet.h

@ -5,7 +5,7 @@
#include <memory> #include <memory>
#include <array> #include <array>
#include <cstring> #include <cstring>
#ifndef NONET #ifdef PACKET_ENCRYPTION
#include <sodium.h> #include <sodium.h>
#endif #endif
@ -35,7 +35,7 @@ typedef uint8_t plr_t;
typedef uint32_t cookie_t; typedef uint32_t cookie_t;
typedef int turn_t; // change int to something else in devilution code later typedef int turn_t; // change int to something else in devilution code later
typedef int leaveinfo_t; // also change later typedef int leaveinfo_t; // also change later
#ifndef NONET #ifdef PACKET_ENCRYPTION
typedef std::array<unsigned char, crypto_secretbox_KEYBYTES> key_t; typedef std::array<unsigned char, crypto_secretbox_KEYBYTES> key_t;
#else #else
// Stub out the key_t defintion as we're not doing any encryption. // Stub out the key_t defintion as we're not doing any encryption.
@ -115,7 +115,7 @@ public:
void process_element(buffer_t &x); void process_element(buffer_t &x);
template <class T> template <class T>
void process_element(T &x); void process_element(T &x);
void Decrypt(); void Decrypt(buffer_t buf);
}; };
class packet_out : public packet_proc<packet_out> { class packet_out : public packet_proc<packet_out> {
@ -132,6 +132,7 @@ public:
static const unsigned char *begin(const T &x); static const unsigned char *begin(const T &x);
template <class T> template <class T>
static const unsigned char *end(const T &x); static const unsigned char *end(const T &x);
static cookie_t GenerateCookie();
void Encrypt(); void Encrypt();
}; };
@ -307,13 +308,13 @@ inline void packet_out::create<PT_DISCONNECT>(plr_t s, plr_t d, plr_t n,
inline void packet_out::process_element(buffer_t &x) inline void packet_out::process_element(buffer_t &x)
{ {
encrypted_buffer.insert(encrypted_buffer.end(), x.begin(), x.end()); decrypted_buffer.insert(decrypted_buffer.end(), x.begin(), x.end());
} }
template <class T> template <class T>
void packet_out::process_element(T &x) void packet_out::process_element(T &x)
{ {
encrypted_buffer.insert(encrypted_buffer.end(), begin(x), end(x)); decrypted_buffer.insert(decrypted_buffer.end(), begin(x), end(x));
} }
template <class T> template <class T>
@ -330,11 +331,13 @@ const unsigned char *packet_out::end(const T &x)
class packet_factory { class packet_factory {
key_t key = {}; key_t key = {};
bool secure;
public: public:
static constexpr unsigned short max_packet_size = 0xFFFF; static constexpr unsigned short max_packet_size = 0xFFFF;
packet_factory(std::string pw = ""); packet_factory();
packet_factory(std::string pw);
std::unique_ptr<packet> make_packet(buffer_t buf); std::unique_ptr<packet> make_packet(buffer_t buf);
template <packet_type t, typename... Args> template <packet_type t, typename... Args>
std::unique_ptr<packet> make_packet(Args... args); std::unique_ptr<packet> make_packet(Args... args);
@ -343,8 +346,16 @@ public:
inline std::unique_ptr<packet> packet_factory::make_packet(buffer_t buf) inline std::unique_ptr<packet> packet_factory::make_packet(buffer_t buf)
{ {
auto ret = std::make_unique<packet_in>(key); auto ret = std::make_unique<packet_in>(key);
#ifndef PACKET_ENCRYPTION
ret->Create(std::move(buf)); ret->Create(std::move(buf));
ret->Decrypt(); #else
if (!secure)
ret->Create(std::move(buf));
else
ret->Decrypt(std::move(buf));
#endif
size_t size = ret->Data().size();
ret->process_data();
return ret; return ret;
} }
@ -353,7 +364,11 @@ std::unique_ptr<packet> packet_factory::make_packet(Args... args)
{ {
auto ret = std::make_unique<packet_out>(key); auto ret = std::make_unique<packet_out>(key);
ret->create<t>(args...); ret->create<t>(args...);
ret->Encrypt(); ret->process_data();
#ifdef PACKET_ENCRYPTION
if (secure)
ret->Encrypt();
#endif
return ret; return ret;
} }

13
Source/dvlnet/tcp_client.cpp

@ -6,7 +6,6 @@
#include <exception> #include <exception>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <sodium.h>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <system_error> #include <system_error>
@ -16,24 +15,23 @@
namespace devilution { namespace devilution {
namespace net { namespace net {
int tcp_client::create(std::string addrstr, std::string passwd) int tcp_client::create(std::string addrstr)
{ {
try { try {
auto port = sgOptions.Network.nPort; auto port = sgOptions.Network.nPort;
local_server = std::make_unique<tcp_server>(ioc, addrstr, port, passwd); local_server = std::make_unique<tcp_server>(ioc, addrstr, port, *pktfty);
return join(local_server->LocalhostSelf(), passwd); return join(local_server->LocalhostSelf());
} catch (std::system_error &e) { } catch (std::system_error &e) {
SDL_SetError("%s", e.what()); SDL_SetError("%s", e.what());
return -1; return -1;
} }
} }
int tcp_client::join(std::string addrstr, std::string passwd) int tcp_client::join(std::string addrstr)
{ {
constexpr int MsSleep = 10; constexpr int MsSleep = 10;
constexpr int NoSleep = 250; constexpr int NoSleep = 250;
setup_password(passwd);
try { try {
std::stringstream port; std::stringstream port;
port << sgOptions.Network.nPort; port << sgOptions.Network.nPort;
@ -46,8 +44,7 @@ int tcp_client::join(std::string addrstr, std::string passwd)
} }
StartReceive(); StartReceive();
{ {
randombytes_buf(reinterpret_cast<unsigned char *>(&cookie_self), cookie_self = packet_out::GenerateCookie();
sizeof(cookie_t));
auto pkt = pktfty->make_packet<PT_JOIN_REQUEST>(PLR_BROADCAST, auto pkt = pktfty->make_packet<PT_JOIN_REQUEST>(PLR_BROADCAST,
PLR_MASTER, cookie_self, PLR_MASTER, cookie_self,
game_init_info); game_init_info);

4
Source/dvlnet/tcp_client.h

@ -17,8 +17,8 @@ namespace net {
class tcp_client : public base { class tcp_client : public base {
public: public:
int create(std::string addrstr, std::string passwd); int create(std::string addrstr);
int join(std::string addrstr, std::string passwd); int join(std::string addrstr);
virtual void poll(); virtual void poll();
virtual void send(packet &pkt); virtual void send(packet &pkt);

4
Source/dvlnet/tcp_server.cpp

@ -12,9 +12,9 @@ namespace devilution {
namespace net { namespace net {
tcp_server::tcp_server(asio::io_context &ioc, const std::string &bindaddr, tcp_server::tcp_server(asio::io_context &ioc, const std::string &bindaddr,
unsigned short port, std::string pw) unsigned short port, packet_factory &pktfty)
: ioc(ioc) : ioc(ioc)
, pktfty(std::move(pw)) , pktfty(pktfty)
{ {
auto addr = asio::ip::address::from_string(bindaddr); auto addr = asio::ip::address::from_string(bindaddr);
auto ep = asio::ip::tcp::endpoint(addr, port); auto ep = asio::ip::tcp::endpoint(addr, port);

4
Source/dvlnet/tcp_server.h

@ -26,7 +26,7 @@ public:
class tcp_server { class tcp_server {
public: public:
tcp_server(asio::io_context &ioc, const std::string &bindaddr, tcp_server(asio::io_context &ioc, const std::string &bindaddr,
unsigned short port, std::string pw); unsigned short port, packet_factory &pktfty);
std::string LocalhostSelf(); std::string LocalhostSelf();
void Close(); void Close();
virtual ~tcp_server(); virtual ~tcp_server();
@ -52,7 +52,7 @@ private:
typedef std::shared_ptr<client_connection> scc; typedef std::shared_ptr<client_connection> scc;
asio::io_context &ioc; asio::io_context &ioc;
packet_factory pktfty; packet_factory &pktfty;
std::unique_ptr<asio::ip::tcp::acceptor> acceptor; std::unique_ptr<asio::ip::tcp::acceptor> acceptor;
std::array<scc, MAX_PLRS> connections; std::array<scc, MAX_PLRS> connections;
buffer_t game_init_info; buffer_t game_init_info;

2
Source/multi.cpp

@ -51,6 +51,7 @@ DWORD sgdwGameLoops;
bool gbIsMultiplayer; bool gbIsMultiplayer;
bool sgbTimeout; bool sgbTimeout;
char szPlayerName[128]; char szPlayerName[128];
bool PublicGame;
BYTE gbDeltaSender; BYTE gbDeltaSender;
bool sgbNetInited; bool sgbNetInited;
uint32_t player_state[MAX_PLRS]; uint32_t player_state[MAX_PLRS];
@ -752,6 +753,7 @@ bool NetInit(bool bSinglePlayer)
nthread_terminate_game("SNetGetGameInfo1"); nthread_terminate_game("SNetGetGameInfo1");
if (!SNetGetGameInfo(GAMEINFO_PASSWORD, szPlayerDescript, 128)) if (!SNetGetGameInfo(GAMEINFO_PASSWORD, szPlayerDescript, 128))
nthread_terminate_game("SNetGetGameInfo2"); nthread_terminate_game("SNetGetGameInfo2");
PublicGame = DvlNet_IsPublicGame();
return true; return true;
} }

1
Source/multi.h

@ -45,6 +45,7 @@ extern GameData sgGameInitInfo;
extern bool gbSelectProvider; extern bool gbSelectProvider;
extern bool gbIsMultiplayer; extern bool gbIsMultiplayer;
extern char szPlayerName[128]; extern char szPlayerName[128];
extern bool PublicGame;
extern BYTE gbDeltaSender; extern BYTE gbDeltaSender;
extern uint32_t player_state[MAX_PLRS]; extern uint32_t player_state[MAX_PLRS];

2
Source/storm/storm.h

@ -293,5 +293,7 @@ void DvlNet_SendInfoRequest();
void DvlNet_ClearGamelist(); void DvlNet_ClearGamelist();
std::vector<std::string> DvlNet_GetGamelist(); std::vector<std::string> DvlNet_GetGamelist();
void DvlNet_SetPassword(std::string pw); void DvlNet_SetPassword(std::string pw);
void DvlNet_ClearPassword();
bool DvlNet_IsPublicGame();
} // namespace devilution } // namespace devilution

27
Source/storm/storm_net.cpp

@ -16,6 +16,7 @@ namespace devilution {
static std::unique_ptr<net::abstract_net> dvlnet_inst; static std::unique_ptr<net::abstract_net> dvlnet_inst;
static char gpszGameName[128] = {}; static char gpszGameName[128] = {};
static char gpszGamePassword[128] = {}; static char gpszGamePassword[128] = {};
static bool GameIsPublic = {};
#ifndef NONET #ifndef NONET
static SdlMutex storm_net_mutex; static SdlMutex storm_net_mutex;
@ -165,8 +166,10 @@ bool SNetCreateGame(const char *pszGameName, const char *pszGamePassword, char *
strncpy(gpszGameName, pszGameName, sizeof(gpszGameName) - 1); strncpy(gpszGameName, pszGameName, sizeof(gpszGameName) - 1);
if (pszGamePassword != nullptr) if (pszGamePassword != nullptr)
strncpy(gpszGamePassword, pszGamePassword, sizeof(gpszGamePassword) - 1); DvlNet_SetPassword(pszGamePassword);
*playerID = dvlnet_inst->create(pszGameName, pszGamePassword); else
DvlNet_ClearPassword();
*playerID = dvlnet_inst->create(pszGameName);
return *playerID != -1; return *playerID != -1;
} }
@ -178,8 +181,10 @@ bool SNetJoinGame(char *pszGameName, char *pszGamePassword, int *playerID)
if (pszGameName != nullptr) if (pszGameName != nullptr)
strncpy(gpszGameName, pszGameName, sizeof(gpszGameName) - 1); strncpy(gpszGameName, pszGameName, sizeof(gpszGameName) - 1);
if (pszGamePassword != nullptr) if (pszGamePassword != nullptr)
strncpy(gpszGamePassword, pszGamePassword, sizeof(gpszGamePassword) - 1); DvlNet_SetPassword(pszGamePassword);
*playerID = dvlnet_inst->join(pszGameName, pszGamePassword); else
DvlNet_ClearPassword();
*playerID = dvlnet_inst->join(pszGameName);
return *playerID != -1; return *playerID != -1;
} }
@ -230,7 +235,21 @@ std::vector<std::string> DvlNet_GetGamelist()
void DvlNet_SetPassword(std::string pw) void DvlNet_SetPassword(std::string pw)
{ {
GameIsPublic = false;
strncpy(gpszGamePassword, pw.c_str(), sizeof(gpszGamePassword) - 1);
dvlnet_inst->setup_password(std::move(pw)); dvlnet_inst->setup_password(std::move(pw));
} }
void DvlNet_ClearPassword()
{
GameIsPublic = true;
gpszGamePassword[0] = '\0';
dvlnet_inst->clear_password();
}
bool DvlNet_IsPublicGame()
{
return GameIsPublic;
}
} // namespace devilution } // namespace devilution

5
vcpkg.json

@ -4,10 +4,13 @@
"dependencies": [ "dependencies": [
"fmt", "fmt",
"libpng", "libpng",
"libsodium",
"sdl2" "sdl2"
], ],
"features": { "features": {
"encryption": {
"description": "Build libsodium for packet encryption",
"dependencies": [ "libsodium" ]
},
"translations": { "translations": {
"description": "Build translation files", "description": "Build translation files",
"dependencies": [ "dependencies": [

Loading…
Cancel
Save