Merge branch 'main' into aether-dimension

This commit is contained in:
Bonnie
2026-03-05 00:12:15 -06:00
207 changed files with 29524 additions and 1310 deletions
+59
View File
@@ -57,6 +57,11 @@
#include "..\Minecraft.World\GenericStats.h"
#endif
#ifdef _WINDOWS64
#include "Xbox\Network\NetworkPlayerXbox.h"
#include "Common\Network\PlatformNetworkManagerStub.h"
#endif
ClientConnection::ClientConnection(Minecraft *minecraft, const wstring& ip, int port)
{
// 4J Stu - No longer used as we use the socket version below.
@@ -758,6 +763,27 @@ void ClientConnection::handleAddPlayer(shared_ptr<AddPlayerPacket> packet)
player->displayName = player->name;
#endif
#ifdef _WINDOWS64
{
PlayerUID pktXuid = player->getXuid();
const PlayerUID WIN64_XUID_BASE = (PlayerUID)0xe000d45248242f2e;
if (pktXuid >= WIN64_XUID_BASE && pktXuid < WIN64_XUID_BASE + MINECRAFT_NET_MAX_PLAYERS)
{
BYTE smallId = (BYTE)(pktXuid - WIN64_XUID_BASE);
INetworkPlayer *np = g_NetworkManager.GetPlayerBySmallId(smallId);
if (np != NULL)
{
NetworkPlayerXbox *npx = (NetworkPlayerXbox *)np;
IQNetPlayer *qp = npx->GetQNetPlayer();
if (qp != NULL && qp->m_gamertag[0] == 0)
{
wcsncpy_s(qp->m_gamertag, 32, packet->name.c_str(), _TRUNCATE);
}
}
}
}
#endif
// printf("\t\t\t\t%d: Add player\n",packet->id,packet->yRot);
int item = packet->carriedItem;
@@ -893,6 +919,39 @@ void ClientConnection::handleMoveEntitySmall(shared_ptr<MoveEntityPacketSmall> p
void ClientConnection::handleRemoveEntity(shared_ptr<RemoveEntitiesPacket> packet)
{
#ifdef _WINDOWS64
if (!g_NetworkManager.IsHost())
{
for (int i = 0; i < packet->ids.length; i++)
{
shared_ptr<Entity> entity = getEntity(packet->ids[i]);
if (entity != NULL && entity->GetType() == eTYPE_PLAYER)
{
shared_ptr<Player> player = dynamic_pointer_cast<Player>(entity);
if (player != NULL)
{
PlayerUID xuid = player->getXuid();
INetworkPlayer *np = g_NetworkManager.GetPlayerByXuid(xuid);
if (np != NULL)
{
NetworkPlayerXbox *npx = (NetworkPlayerXbox *)np;
IQNetPlayer *qp = npx->GetQNetPlayer();
if (qp != NULL)
{
extern CPlatformNetworkManagerStub *g_pPlatformNetworkManager;
g_pPlatformNetworkManager->NotifyPlayerLeaving(qp);
qp->m_smallId = 0;
qp->m_isRemote = false;
qp->m_isHostPlayer = false;
qp->m_gamertag[0] = 0;
qp->SetCustomDataValue(0);
}
}
}
}
}
}
#endif
for (int i = 0; i < packet->ids.length; i++)
{
level->removeEntity(packet->ids[i]);
+6 -1
View File
@@ -1220,12 +1220,17 @@ void CMinecraftApp::ActionGameSettings(int iPad,eGameSetting eVal)
}
break;
case eGameSetting_Gamma:
#ifdef _WINDOWS64
pMinecraft->options->set(Options::Option::GAMMA, ((float)GameSettingsA[iPad]->ucGamma) / 100.0f);
#else
if(iPad==ProfileManager.GetPrimaryPad())
{
// ucGamma range is 0-100, UpdateGamma is 0 - 32768
float fVal=((float)GameSettingsA[iPad]->ucGamma)*327.68f;
RenderManager.UpdateGamma((unsigned short)fVal);
}
}
#endif
break;
case eGameSetting_Difficulty:
@@ -40,10 +40,6 @@
#include "..\Minecraft.World\DurangoStats.h"
#endif
#ifdef _WINDOWS64
#include "..\..\Windows64\Network\P2PConnectionManagerWin.h"
#endif
// Global instance
CGameNetworkManager g_NetworkManager;
CPlatformNetworkManager *CGameNetworkManager::s_pPlatformNetworkManager;
@@ -62,10 +58,6 @@ CGameNetworkManager::CGameNetworkManager()
m_pUpsell = NULL;
m_pInviteInfo = NULL;
#endif
#ifdef _WINDOWS64
m_p2pManager = NULL;
#endif
}
void CGameNetworkManager::Initialise()
@@ -91,9 +83,6 @@ void CGameNetworkManager::Terminate()
{
if( m_bInitialised )
{
#ifdef _WINDOWS64
TeardownP2PMesh();
#endif
s_pPlatformNetworkManager->Terminate();
}
}
@@ -129,10 +118,6 @@ void CGameNetworkManager::DoWork()
#endif
s_pPlatformNetworkManager->DoWork();
#ifdef _WINDOWS64
TickP2P();
#endif
#ifdef __ORBIS__
if (m_pUpsell != NULL && m_pUpsell->hasResponse())
{
@@ -181,16 +166,14 @@ bool CGameNetworkManager::_RunNetworkGame(LPVOID lpParameter)
return true;
}
}
else
{
// Client needs QNET_STATE_GAME_PLAY so that IsInGameplay() returns true
s_pPlatformNetworkManager->SetGamePlayState();
}
if( g_NetworkManager.IsLeavingGame() ) return false;
#ifdef _WINDOWS64
if (!g_NetworkManager.IsLocalGame())
{
g_NetworkManager.EstablishP2PMesh();
}
#endif
app.SetGameStarted(true);
// 4J-PB - if this is the trial game, start the trial timer
@@ -1283,15 +1266,7 @@ bool CGameNetworkManager::SystemFlagGet(INetworkPlayer *pNetworkPlayer, int inde
wstring CGameNetworkManager::GatherStats()
{
wstring stats = s_pPlatformNetworkManager->GatherStats();
#ifdef _WINDOWS64
if (m_p2pManager != NULL)
{
stats += L"\n";
stats += GatherP2PStats();
}
#endif
return stats;
return s_pPlatformNetworkManager->GatherStats();
}
void CGameNetworkManager::renderQueueMeter()
@@ -1421,7 +1396,10 @@ void CGameNetworkManager::CreateSocket( INetworkPlayer *pNetworkPlayer, bool loc
Minecraft *pMinecraft = Minecraft::GetInstance();
Socket *socket = NULL;
shared_ptr<MultiplayerLocalPlayer> mpPlayer = pMinecraft->localplayers[pNetworkPlayer->GetUserIndex()];
shared_ptr<MultiplayerLocalPlayer> mpPlayer = nullptr;
int userIdx = pNetworkPlayer->GetUserIndex();
if (userIdx >= 0 && userIdx < XUSER_MAX_COUNT)
mpPlayer = pMinecraft->localplayers[userIdx];
if( localPlayer && mpPlayer != NULL && mpPlayer->connection != NULL)
{
// If we already have a MultiplayerLocalPlayer here then we are doing a session type change
@@ -1518,7 +1496,7 @@ void CGameNetworkManager::PlayerJoining( INetworkPlayer *pNetworkPlayer )
else
{
if( !pNetworkPlayer->IsHost() )
{
{
for(int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
{
if(Minecraft::GetInstance()->localplayers[idx] != NULL)
@@ -1529,29 +1507,10 @@ void CGameNetworkManager::PlayerJoining( INetworkPlayer *pNetworkPlayer )
}
}
#endif
#ifdef _WINDOWS64
if (m_p2pManager != NULL && !pNetworkPlayer->IsLocal())
{
INetworkPlayer* localPlayer = GetLocalPlayerByUserIndex(0);
if (localPlayer != NULL)
{
m_p2pManager->EstablishDirectConnection(localPlayer, pNetworkPlayer);
}
}
#endif
}
void CGameNetworkManager::PlayerLeaving( INetworkPlayer *pNetworkPlayer )
{
#ifdef _WINDOWS64
// Disconnect P2P with the leaving player
if (m_p2pManager != NULL && !pNetworkPlayer->IsLocal())
{
m_p2pManager->DisconnectPeer(pNetworkPlayer);
}
#endif
if( pNetworkPlayer->IsLocal() )
{
ProfileManager.SetCurrentGameActivity(pNetworkPlayer->GetUserIndex(),CONTEXT_PRESENCE_IDLE,false);
@@ -2047,101 +2006,3 @@ void CGameNetworkManager::startAdhocMatching()
}
#endif
#ifdef _WINDOWS64
///////////////////////////////////// P2P Networking /////////////////////////////////////
void CGameNetworkManager::InitializeP2PConnections()
{
if (m_p2pManager != NULL)
return; // Already initialized
CP2PConnectionManagerWin* winP2P = new CP2PConnectionManagerWin();
if (winP2P->Initialize())
{
m_p2pManager = winP2P;
// Wire up the P2P manager to the socket layer and packet router
Socket::SetP2PManager(m_p2pManager);
g_PacketRouter.Initialize(m_p2pManager);
app.DebugPrintf("P2P: Network manager initialized\n");
}
else
{
delete winP2P;
app.DebugPrintf("P2P: Failed to initialize network manager\n");
}
}
void CGameNetworkManager::EstablishP2PMesh()
{
if (m_p2pManager == NULL)
InitializeP2PConnections();
if (m_p2pManager == NULL)
return;
// Discover our public endpoint
m_p2pManager->DiscoverPublicEndpoint();
INetworkPlayer* localPlayer = GetLocalPlayerByUserIndex(0);
if (localPlayer == NULL)
return;
int playerCount = GetPlayerCount();
for (int i = 0; i < playerCount; i++)
{
INetworkPlayer* player = GetPlayerByIndex(i);
if (player != NULL && !player->IsLocal() && player != localPlayer)
{
m_p2pManager->EstablishDirectConnection(localPlayer, player);
}
}
app.DebugPrintf("P2P: Mesh establishment initiated for %d players\n", playerCount);
}
void CGameNetworkManager::TeardownP2PMesh()
{
if (m_p2pManager == NULL)
return;
// Disconnect the P2P manager from socket layer and packet router
Socket::SetP2PManager(NULL);
g_PacketRouter.Shutdown();
// Shutdown and delete
m_p2pManager->Shutdown();
delete m_p2pManager;
m_p2pManager = NULL;
app.DebugPrintf("P2P: Mesh torn down\n");
}
void CGameNetworkManager::TickP2P()
{
if (m_p2pManager != NULL)
{
m_p2pManager->Tick();
}
}
EP2PConnectionState CGameNetworkManager::GetP2PConnectionState(INetworkPlayer* player)
{
if (m_p2pManager == NULL || player == NULL)
return P2P_STATE_DISCONNECTED;
return m_p2pManager->GetConnectionState(player);
}
wstring CGameNetworkManager::GatherP2PStats()
{
if (m_p2pManager == NULL)
return L"P2P: Not active\n";
return m_p2pManager->GetDebugStats();
}
#endif // _WINDOWS64
@@ -15,11 +15,6 @@ using namespace std;
#endif
#include "SessionInfo.h"
#ifdef _WINDOWS64
#include "P2PConnectionManager.h"
#include "PacketRouter.h"
#endif
#ifdef __ORBIS__
#include "..\..\Orbis\Network\PsPlusUpsellWrapper_Orbis.h"
#endif
@@ -164,17 +159,6 @@ public:
void renderQueueMeter();
wstring GatherRTTStats();
#ifdef _WINDOWS64
void InitializeP2PConnections();
void EstablishP2PMesh();
void TeardownP2PMesh();
void TickP2P();
IP2PConnectionManager* GetP2PManager() { return m_p2pManager; }
EP2PConnectionState GetP2PConnectionState(INetworkPlayer* player);
bool IsP2PActive() { return m_p2pManager != NULL; }
wstring GatherP2PStats();
#endif
// GUI debug output
// Used for debugging output
@@ -217,21 +201,20 @@ private:
C4JThread::Event* m_hServerReadyEvent;
bool m_bInitialised;
public:
static CPlatformNetworkManager *s_pPlatformNetworkManager;
#ifdef _XBOX_ONE
public:
void SetFullSessionMessageOnNextSessionChange() { m_bFullSessionMessageOnNextSessionChange = true; }
#endif
private:
float m_lastPlayerEventTimeStart; // For telemetry
static CPlatformNetworkManager *s_pPlatformNetworkManager;
bool m_bNetworkThreadRunning;
int GetJoiningReadyPercentage();
bool m_bLastDisconnectWasLostRoomOnly;
bool m_bFullSessionMessageOnNextSessionChange;
#ifdef _WINDOWS64
IP2PConnectionManager* m_p2pManager;
#endif
#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__
bool m_bSignedOutofPSN;
#endif
@@ -1,105 +0,0 @@
#include "stdafx.h"
#include "PacketRouter.h"
PacketRouter g_PacketRouter;
PacketRouter::PacketRouter()
{
m_p2pManager = NULL;
m_p2pEnabled = false;
m_p2pRoutedCount = 0;
m_hostRoutedCount = 0;
}
void PacketRouter::Initialize(IP2PConnectionManager* p2pManager)
{
m_p2pManager = p2pManager;
m_p2pEnabled = (p2pManager != NULL);
}
void PacketRouter::Shutdown()
{
m_p2pManager = NULL;
m_p2pEnabled = false;
ResetStats();
}
ERouteDecision PacketRouter::GetRouteDecision(shared_ptr<Packet> packet, INetworkPlayer* targetPlayer)
{
// If P2P is disabled or no manager, always use host
if (!m_p2pEnabled || m_p2pManager == NULL || targetPlayer == NULL)
return ROUTE_VIA_HOST;
// Local players don't need P2P
if (targetPlayer->IsLocal())
return ROUTE_VIA_HOST;
int packetId = packet->getId();
EPacketRoutingMode routingMode = Packet::GetRoutingMode(packetId);
switch (routingMode)
{
case ROUTING_HOST_ONLY:
return ROUTE_VIA_HOST;
case ROUTING_PREFER_DIRECT:
// Use P2P if we have a direct connection to this player
if (m_p2pManager->IsDirectConnectionAvailable(targetPlayer))
return ROUTE_VIA_P2P;
return ROUTE_VIA_HOST;
case ROUTING_DIRECT_ONLY:
if (m_p2pManager->IsDirectConnectionAvailable(targetPlayer))
return ROUTE_VIA_P2P;
return ROUTE_VIA_HOST; // Fallback to host if no direct link
case ROUTING_BROADCAST:
return ROUTE_VIA_HOST; // Broadcasts always go through host
default:
return ROUTE_VIA_HOST;
}
}
bool PacketRouter::RoutePacket(shared_ptr<Packet> packet, INetworkPlayer* targetPlayer,
const void* serializedData, int dataSize)
{
ERouteDecision decision = GetRouteDecision(packet, targetPlayer);
if (decision == ROUTE_VIA_P2P && m_p2pManager != NULL)
{
EP2PChannel channel = GetChannelForPacket(packet->getId());
m_p2pManager->SendDirect(targetPlayer, serializedData, dataSize, channel);
m_p2pRoutedCount++;
return true;
}
m_hostRoutedCount++;
return false; // Caller should use existing host path
}
EP2PChannel PacketRouter::GetChannelForPacket(int packetId)
{
// Movement packets
if ((packetId >= 10 && packetId <= 13) || // MovePlayerPacket variants
(packetId >= 30 && packetId <= 34) || // MoveEntityPacket variants
(packetId >= 162 && packetId <= 165)) // MoveEntityPacketSmall variants
{
return P2P_CHANNEL_MOVEMENT;
}
// Animation packets
if (packetId == 18 || packetId == 35) // AnimatePacket, RotateHeadPacket
{
return P2P_CHANNEL_ANIMATION;
}
// Effects/sounds
if (packetId == 62) // LevelSoundPacket
{
return P2P_CHANNEL_EFFECTS;
}
// Default
return P2P_CHANNEL_MOVEMENT;
}
@@ -1,49 +0,0 @@
#pragma once
#include "..\..\..\Minecraft.World\Packet.h"
#include "P2PConnectionManager.h"
#include "NetworkPlayerInterface.h"
// Packet routing decision
enum ERouteDecision
{
ROUTE_VIA_HOST = 0, // Send through host TCP (existing path)
ROUTE_VIA_P2P, // Send directly via P2P UDP
ROUTE_VIA_BOTH // Send via both paths (redundancy for critical packets)
};
class PacketRouter
{
public:
PacketRouter();
// Initialize with P2P manager reference
void Initialize(IP2PConnectionManager* p2pManager);
void Shutdown();
// Determine how to route a packet to a specific player
ERouteDecision GetRouteDecision(shared_ptr<Packet> packet, INetworkPlayer* targetPlayer);
// Route a serialized packet - returns true if sent via P2P
bool RoutePacket(shared_ptr<Packet> packet, INetworkPlayer* targetPlayer,
const void* serializedData, int dataSize);
// Enable/disable P2P routing (global toggle)
void SetP2PEnabled(bool enabled) { m_p2pEnabled = enabled; }
bool IsP2PEnabled() const { return m_p2pEnabled; }
// Map packet IDs to P2P channels
static EP2PChannel GetChannelForPacket(int packetId);
// Stats
int GetP2PRoutedCount() const { return m_p2pRoutedCount; }
int GetHostRoutedCount() const { return m_hostRoutedCount; }
void ResetStats() { m_p2pRoutedCount = 0; m_hostRoutedCount = 0; }
private:
IP2PConnectionManager* m_p2pManager;
bool m_p2pEnabled;
int m_p2pRoutedCount;
int m_hostRoutedCount;
};
extern PacketRouter g_PacketRouter;
@@ -71,6 +71,7 @@ public:
virtual void HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0) = 0;
virtual int JoinGame(FriendSessionInfo *searchResult, int dwLocalUsersMask, int dwPrimaryUserIndex ) = 0;
virtual void CancelJoinGame() {};
virtual void SetGamePlayState() {};
virtual bool SetLocalGame(bool isLocal) = 0;
virtual bool IsLocalGame() = 0;
virtual void SetPrivateGame(bool isPrivate) = 0;
@@ -1,8 +1,14 @@
#include "stdafx.h"
#include "stdafx.h"
#include "..\..\..\Minecraft.World\Socket.h"
#include "..\..\..\Minecraft.World\StringHelpers.h"
#include "PlatformNetworkManagerStub.h"
#include "..\..\Xbox\Network\NetworkPlayerXbox.h" // TODO - stub version of this?
#ifdef _WINDOWS64
#include "..\..\Windows64\Network\WinsockNetLayer.h"
#include "..\..\Minecraft.h"
#include "..\..\User.h"
#include "Extrax64Stubs.h"
#endif
CPlatformNetworkManagerStub *g_pPlatformNetworkManager;
@@ -72,7 +78,7 @@ void CPlatformNetworkManagerStub::NotifyPlayerJoined(IQNetPlayer *pQNetPlayer )
}
}
g_NetworkManager.PlayerJoining( networkPlayer );
if( createFakeSocket == true && !m_bHostChanged )
{
g_NetworkManager.CreateSocket( networkPlayer, localPlayer );
@@ -92,7 +98,7 @@ void CPlatformNetworkManagerStub::NotifyPlayerJoined(IQNetPlayer *pQNetPlayer )
// g_NetworkManager.UpdateAndSetGameSessionData();
SystemFlagAddPlayer( networkPlayer );
}
for( int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
{
if(playerChangedCallback[idx] != NULL)
@@ -114,10 +120,42 @@ void CPlatformNetworkManagerStub::NotifyPlayerJoined(IQNetPlayer *pQNetPlayer )
}
}
void CPlatformNetworkManagerStub::NotifyPlayerLeaving(IQNetPlayer *pQNetPlayer)
{
app.DebugPrintf("Player 0x%p \"%ls\" leaving.\n", pQNetPlayer, pQNetPlayer->GetGamertag());
INetworkPlayer *networkPlayer = getNetworkPlayer(pQNetPlayer);
if (networkPlayer == NULL)
return;
Socket *socket = networkPlayer->GetSocket();
if (socket != NULL)
{
if (m_pIQNet->IsHost())
g_NetworkManager.CloseConnection(networkPlayer);
}
if (m_pIQNet->IsHost())
{
SystemFlagRemovePlayer(networkPlayer);
}
g_NetworkManager.PlayerLeaving(networkPlayer);
for (int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
{
if (playerChangedCallback[idx] != NULL)
playerChangedCallback[idx](playerChangedCallbackParam[idx], networkPlayer, true);
}
removeNetworkPlayer(pQNetPlayer);
}
bool CPlatformNetworkManagerStub::Initialise(CGameNetworkManager *pGameNetworkManager, int flagIndexSize)
{
m_pGameNetworkManager = pGameNetworkManager;
m_flagIndexSize = flagIndexSize;
m_pIQNet = new IQNet();
g_pPlatformNetworkManager = this;
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
{
@@ -148,19 +186,12 @@ bool CPlatformNetworkManagerStub::Initialise(CGameNetworkManager *pGameNetworkMa
m_currentSearchResultsCount[i] = 0;
}
#ifdef _WINDOWS64
m_lanSessionManager.Initialize();
#endif
// Success!
return true;
}
void CPlatformNetworkManagerStub::Terminate()
{
#ifdef _WINDOWS64
m_lanSessionManager.Shutdown();
#endif
}
int CPlatformNetworkManagerStub::GetJoiningReadyPercentage()
@@ -182,8 +213,61 @@ bool CPlatformNetworkManagerStub::isSystemPrimaryPlayer(IQNetPlayer *pQNetPlayer
void CPlatformNetworkManagerStub::DoWork()
{
#ifdef _WINDOWS64
m_lanSessionManager.Tick();
TickSearch();
extern QNET_STATE _iQNetStubState;
if (_iQNetStubState == QNET_STATE_SESSION_STARTING && app.GetGameStarted())
{
_iQNetStubState = QNET_STATE_GAME_PLAY;
if (m_pIQNet->IsHost())
{
WinsockNetLayer::UpdateAdvertiseJoinable(true);
}
}
if (_iQNetStubState == QNET_STATE_IDLE)
TickSearch();
if (_iQNetStubState == QNET_STATE_GAME_PLAY && m_pIQNet->IsHost())
{
BYTE disconnectedSmallId;
while (WinsockNetLayer::PopDisconnectedSmallId(&disconnectedSmallId))
{
IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerBySmallId(disconnectedSmallId);
if (qnetPlayer != NULL && qnetPlayer->m_smallId == disconnectedSmallId)
{
NotifyPlayerLeaving(qnetPlayer);
qnetPlayer->m_smallId = 0;
qnetPlayer->m_isRemote = false;
qnetPlayer->m_isHostPlayer = false;
qnetPlayer->m_gamertag[0] = 0;
qnetPlayer->SetCustomDataValue(0);
WinsockNetLayer::PushFreeSmallId(disconnectedSmallId);
if (IQNet::s_playerCount > 1)
IQNet::s_playerCount--;
}
}
for (int i = 1; i < MINECRAFT_NET_MAX_PLAYERS; i++)
{
IQNetPlayer *qp = &IQNet::m_player[i];
if (qp->GetCustomDataValue() == 0)
continue;
INetworkPlayer *np = (INetworkPlayer *)qp->GetCustomDataValue();
Socket *sock = np->GetSocket();
if (sock != NULL && sock->isClosing())
{
WinsockNetLayer::CloseConnectionBySmallId((BYTE)i);
}
}
}
if (_iQNetStubState == QNET_STATE_GAME_PLAY && !m_pIQNet->IsHost())
{
if (!WinsockNetLayer::IsConnected() && !g_NetworkManager.IsLeavingGame())
{
if (app.GetDisconnectReason() == DisconnectPacket::eDisconnect_None)
app.SetDisconnectReason(DisconnectPacket::eDisconnect_Quitting);
app.SetAction(ProfileManager.GetPrimaryPad(), eAppAction_ExitWorld, (void *)TRUE);
}
}
#endif
}
@@ -244,16 +328,31 @@ bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost)
m_bLeavingGame = true;
#ifdef _WINDOWS64
m_lanSessionManager.StopBroadcasting();
WinsockNetLayer::StopAdvertising();
#endif
// If we are the host wait for the game server to end
if(m_pIQNet->IsHost() && g_NetworkManager.ServerStoppedValid())
{
m_pIQNet->EndGame();
g_NetworkManager.ServerStoppedWait();
g_NetworkManager.ServerStoppedDestroy();
}
else
{
m_pIQNet->EndGame();
}
for (AUTO_VAR(it, currentNetworkPlayers.begin()); it != currentNetworkPlayers.end(); it++)
delete *it;
currentNetworkPlayers.clear();
m_machineQNetPrimaryPlayers.clear();
SystemFlagReset();
#ifdef _WINDOWS64
WinsockNetLayer::Shutdown();
WinsockNetLayer::Initialize();
#endif
return true;
}
@@ -264,31 +363,38 @@ bool CPlatformNetworkManagerStub::_LeaveGame(bool bMigrateHost, bool bLeaveRoom)
void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots /*= MINECRAFT_NET_MAX_PLAYERS*/, unsigned char privateSlots /*= 0*/)
{
// #ifdef _XBOX
// 4J Stu - We probably did this earlier as well, but just to be sure!
SetLocalGame( !bOnlineGame );
SetPrivateGame( bIsPrivate );
SystemFlagReset();
// Make sure that the Primary Pad is in by default
localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() );
m_bLeavingGame = false;
m_pIQNet->HostGame();
#ifdef _WINDOWS64
IQNet::m_player[0].m_smallId = 0;
IQNet::m_player[0].m_isRemote = false;
IQNet::m_player[0].m_isHostPlayer = true;
IQNet::s_playerCount = 1;
#endif
_HostGame( localUsersMask, publicSlots, privateSlots );
//#endif
#ifdef _WINDOWS64
int port = WIN64_NET_DEFAULT_PORT;
if (!WinsockNetLayer::IsActive())
WinsockNetLayer::HostGame(port);
const wchar_t *hostName = IQNet::m_player[0].m_gamertag;
unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
#endif
}
void CPlatformNetworkManagerStub::_HostGame(int usersMask, unsigned char publicSlots /*= MINECRAFT_NET_MAX_PLAYERS*/, unsigned char privateSlots /*= 0*/)
{
#ifdef _WINDOWS64
// Start LAN broadcasting so other instances can discover this game
char* gamertag = ProfileManager.GetGamertag(0);
m_lanSessionManager.StartBroadcasting(m_hostGameSessionData, gamertag, "Minecraft World");
app.DebugPrintf("LAN: Host game started, broadcasting session\n");
#endif
}
bool CPlatformNetworkManagerStub::_StartGame()
@@ -298,7 +404,52 @@ bool CPlatformNetworkManagerStub::_StartGame()
int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo *searchResult, int localUsersMask, int primaryUserIndex)
{
#ifdef _WINDOWS64
if (searchResult == NULL)
return CGameNetworkManager::JOINGAME_FAIL_GENERAL;
const char *hostIP = searchResult->data.hostIP;
int hostPort = searchResult->data.hostPort;
if (hostPort <= 0 || hostIP[0] == 0)
return CGameNetworkManager::JOINGAME_FAIL_GENERAL;
m_bLeavingGame = false;
IQNet::s_isHosting = false;
m_pIQNet->ClientJoinGame();
IQNet::m_player[0].m_smallId = 0;
IQNet::m_player[0].m_isRemote = true;
IQNet::m_player[0].m_isHostPlayer = true;
wcsncpy_s(IQNet::m_player[0].m_gamertag, 32, searchResult->data.hostName, _TRUNCATE);
WinsockNetLayer::StopDiscovery();
if (!WinsockNetLayer::JoinGame(hostIP, hostPort))
{
app.DebugPrintf("Win64 LAN: Failed to connect to %s:%d\n", hostIP, hostPort);
return CGameNetworkManager::JOINGAME_FAIL_GENERAL;
}
BYTE localSmallId = WinsockNetLayer::GetLocalSmallId();
IQNet::m_player[localSmallId].m_smallId = localSmallId;
IQNet::m_player[localSmallId].m_isRemote = false;
IQNet::m_player[localSmallId].m_isHostPlayer = false;
Minecraft *pMinecraft = Minecraft::GetInstance();
wcscpy_s(IQNet::m_player[localSmallId].m_gamertag, 32, pMinecraft->user->name.c_str());
IQNet::s_playerCount = localSmallId + 1;
NotifyPlayerJoined(&IQNet::m_player[0]);
NotifyPlayerJoined(&IQNet::m_player[localSmallId]);
m_pGameNetworkManager->StateChange_AnyToStarting();
return CGameNetworkManager::JOINGAME_SUCCESS;
#else
return CGameNetworkManager::JOINGAME_SUCCESS;
#endif
}
bool CPlatformNetworkManagerStub::SetLocalGame(bool isLocal)
@@ -331,54 +482,40 @@ void CPlatformNetworkManagerStub::UnRegisterPlayerChangedCallback(int iPad, void
void CPlatformNetworkManagerStub::HandleSignInChange()
{
return;
return;
}
void CPlatformNetworkManagerStub::SetGamePlayState()
{
#ifdef _WINDOWS64
extern QNET_STATE _iQNetStubState;
_iQNetStubState = QNET_STATE_GAME_PLAY;
#endif
}
bool CPlatformNetworkManagerStub::_RunNetworkGame()
{
#ifdef _WINDOWS64
extern QNET_STATE _iQNetStubState;
_iQNetStubState = QNET_STATE_GAME_PLAY;
for (DWORD i = 0; i < IQNet::s_playerCount; i++)
{
if (IQNet::m_player[i].m_isRemote)
{
INetworkPlayer *pNetworkPlayer = getNetworkPlayer(&IQNet::m_player[i]);
if (pNetworkPlayer != NULL && pNetworkPlayer->GetSocket() != NULL)
{
Socket::addIncomingSocket(pNetworkPlayer->GetSocket());
}
}
}
#endif
return true;
}
void CPlatformNetworkManagerStub::UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving /*= NULL*/)
{
// DWORD playerCount = m_pIQNet->GetPlayerCount();
//
// if( this->m_bLeavingGame )
// return;
//
// if( GetHostPlayer() == NULL )
// return;
//
// for(unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i)
// {
// if( i < playerCount )
// {
// INetworkPlayer *pNetworkPlayer = GetPlayerByIndex(i);
//
// // We can call this from NotifyPlayerLeaving but at that point the player is still considered in the session
// if( pNetworkPlayer != pNetworkPlayerLeaving )
// {
// m_hostGameSessionData.players[i] = ((NetworkPlayerXbox *)pNetworkPlayer)->GetUID();
//
// char *temp;
// temp = (char *)wstringtofilename( pNetworkPlayer->GetOnlineName() );
// memcpy(m_hostGameSessionData.szPlayers[i],temp,XUSER_NAME_SIZE);
// }
// else
// {
// m_hostGameSessionData.players[i] = NULL;
// memset(m_hostGameSessionData.szPlayers[i],0,XUSER_NAME_SIZE);
// }
// }
// else
// {
// m_hostGameSessionData.players[i] = NULL;
// memset(m_hostGameSessionData.szPlayers[i],0,XUSER_NAME_SIZE);
// }
// }
//
// m_hostGameSessionData.hostPlayerUID = ((NetworkPlayerXbox *)GetHostPlayer())->GetQNetPlayer()->GetXuid();
// m_hostGameSessionData.m_uiGameHostSettings = app.GetGameHostOption(eGameHostOption_All);
}
int CPlatformNetworkManagerStub::RemovePlayerOnSocketClosedThreadProc( void* lpParam )
@@ -525,28 +662,59 @@ wstring CPlatformNetworkManagerStub::GatherRTTStats()
void CPlatformNetworkManagerStub::TickSearch()
{
#ifdef _WINDOWS64
// Start listening if we're not already and not hosting
if (!m_lanSessionManager.IsListening() && !m_lanSessionManager.IsBroadcasting())
{
m_lanSessionManager.StartListening();
}
if (m_SessionsUpdatedCallback == NULL)
return;
// If new sessions were discovered, notify the UI
if (m_lanSessionManager.HasNewSessions())
{
m_lanSessionManager.ClearNewSessionsFlag();
m_bSearchResultsReady = true;
static DWORD lastSearchTime = 0;
DWORD now = GetTickCount();
if (now - lastSearchTime < 2000)
return;
lastSearchTime = now;
if (m_SessionsUpdatedCallback != NULL)
{
m_SessionsUpdatedCallback(m_pSearchParam);
}
}
SearchForGames();
#endif
}
void CPlatformNetworkManagerStub::SearchForGames()
{
#ifdef _WINDOWS64
std::vector<Win64LANSession> lanSessions = WinsockNetLayer::GetDiscoveredSessions();
for (size_t i = 0; i < friendsSessions[0].size(); i++)
delete friendsSessions[0][i];
friendsSessions[0].clear();
for (size_t i = 0; i < lanSessions.size(); i++)
{
FriendSessionInfo *info = new FriendSessionInfo();
size_t nameLen = wcslen(lanSessions[i].hostName);
info->displayLabel = new wchar_t[nameLen + 1];
wcscpy_s(info->displayLabel, nameLen + 1, lanSessions[i].hostName);
info->displayLabelLength = (unsigned char)nameLen;
info->displayLabelViewableStartIndex = 0;
info->data.netVersion = lanSessions[i].netVersion;
info->data.m_uiGameHostSettings = lanSessions[i].gameHostSettings;
info->data.texturePackParentId = lanSessions[i].texturePackParentId;
info->data.subTexturePackId = lanSessions[i].subTexturePackId;
info->data.isReadyToJoin = lanSessions[i].isJoinable;
info->data.isJoinable = lanSessions[i].isJoinable;
strncpy_s(info->data.hostIP, sizeof(info->data.hostIP), lanSessions[i].hostIP, _TRUNCATE);
info->data.hostPort = lanSessions[i].hostPort;
wcsncpy_s(info->data.hostName, XUSER_NAME_SIZE, lanSessions[i].hostName, _TRUNCATE);
info->data.playerCount = lanSessions[i].playerCount;
info->data.maxPlayers = lanSessions[i].maxPlayers;
info->sessionId = (SessionID)((unsigned __int64)inet_addr(lanSessions[i].hostIP) | ((unsigned __int64)lanSessions[i].hostPort << 32));
friendsSessions[0].push_back(info);
}
m_searchResultsCount[0] = (int)friendsSessions[0].size();
if (m_SessionsUpdatedCallback != NULL)
m_SessionsUpdatedCallback(m_pSearchParam);
#endif
}
int CPlatformNetworkManagerStub::SearchForGamesThreadProc( void* lpParameter )
@@ -562,12 +730,10 @@ void CPlatformNetworkManagerStub::SetSearchResultsReady(int resultCount)
vector<FriendSessionInfo *> *CPlatformNetworkManagerStub::GetSessionList(int iPad, int localPlayers, bool partyOnly)
{
#ifdef _WINDOWS64
return m_lanSessionManager.GetSessionList();
#else
vector<FriendSessionInfo *> *filteredList = new vector<FriendSessionInfo *>();
for (size_t i = 0; i < friendsSessions[0].size(); i++)
filteredList->push_back(friendsSessions[0][i]);
return filteredList;
#endif
}
bool CPlatformNetworkManagerStub::GetGameSessionInfo(int iPad, SessionID sessionId, FriendSessionInfo *foundSessionInfo)
@@ -596,15 +762,6 @@ void CPlatformNetworkManagerStub::ForceFriendsSessionRefresh()
delete m_pSearchResults[i];
m_pSearchResults[i] = NULL;
}
#ifdef _WINDOWS64
// Restart LAN listening to force a fresh discovery
if (m_lanSessionManager.IsListening())
{
m_lanSessionManager.StopListening();
m_lanSessionManager.StartListening();
}
#endif
}
INetworkPlayer *CPlatformNetworkManagerStub::addNetworkPlayer(IQNetPlayer *pQNetPlayer)
@@ -636,7 +793,7 @@ INetworkPlayer *CPlatformNetworkManagerStub::getNetworkPlayer(IQNetPlayer *pQNet
INetworkPlayer *CPlatformNetworkManagerStub::GetLocalPlayerByUserIndex(int userIndex )
{
return getNetworkPlayer(m_pIQNet->GetLocalPlayerByUserIndex(userIndex));
return getNetworkPlayer(m_pIQNet->GetLocalPlayerByUserIndex(userIndex));
}
INetworkPlayer *CPlatformNetworkManagerStub::GetPlayerByIndex(int playerIndex)
@@ -651,7 +808,21 @@ INetworkPlayer * CPlatformNetworkManagerStub::GetPlayerByXuid(PlayerUID xuid)
INetworkPlayer * CPlatformNetworkManagerStub::GetPlayerBySmallId(unsigned char smallId)
{
return getNetworkPlayer(m_pIQNet->GetPlayerBySmallId(smallId));
IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerBySmallId(smallId);
if (qnetPlayer == NULL)
return NULL;
INetworkPlayer *networkPlayer = getNetworkPlayer(qnetPlayer);
#ifdef _WINDOWS64
if (networkPlayer == NULL && smallId != 0 && !m_pIQNet->IsHost())
{
qnetPlayer->m_isRemote = true;
qnetPlayer->m_isHostPlayer = false;
NotifyPlayerJoined(qnetPlayer);
networkPlayer = getNetworkPlayer(qnetPlayer);
}
#endif
return networkPlayer;
}
INetworkPlayer *CPlatformNetworkManagerStub::GetHostPlayer()
@@ -6,10 +6,6 @@ using namespace std;
#include "PlatformNetworkManagerInterface.h"
#include "SessionInfo.h"
#ifdef _WINDOWS64
#include "LANSessionManager.h"
#endif
class CPlatformNetworkManagerStub : public CPlatformNetworkManager
{
friend class CGameNetworkManager;
@@ -59,15 +55,16 @@ public:
virtual void HandleSignInChange();
virtual bool _RunNetworkGame();
virtual void SetGamePlayState();
private:
bool isSystemPrimaryPlayer(IQNetPlayer *pQNetPlayer);
virtual bool _LeaveGame(bool bMigrateHost, bool bLeaveRoom);
virtual void _HostGame(int dwUsersMask, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0);
virtual bool _StartGame();
public:
IQNet * m_pIQNet; // pointer to QNet interface
private:
HANDLE m_notificationListener;
vector<IQNetPlayer *> m_machineQNetPrimaryPlayers; // collection of players that we deem to be the main one for that system
@@ -165,16 +162,11 @@ public:
virtual void GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam );
virtual void ForceFriendsSessionRefresh();
private:
public:
void NotifyPlayerJoined( IQNetPlayer *pQNetPlayer );
void NotifyPlayerLeaving( IQNetPlayer *pQNetPlayer );
#ifndef _XBOX
void FakeLocalPlayerJoined() { NotifyPlayerJoined(m_pIQNet->GetLocalPlayerByUserIndex(0)); }
#endif
#ifdef _WINDOWS64
CLANSessionManager* GetLANSessionManager() { return &m_lanSessionManager; }
private:
CLANSessionManager m_lanSessionManager;
#endif
};
+19 -5
View File
@@ -63,12 +63,19 @@ typedef struct _GameSessionData
#else
typedef struct _GameSessionData
{
unsigned short netVersion; // 2 bytes
unsigned int m_uiGameHostSettings; // 4 bytes
unsigned int texturePackParentId; // 4 bytes
unsigned char subTexturePackId; // 1 byte
unsigned short netVersion;
unsigned int m_uiGameHostSettings;
unsigned int texturePackParentId;
unsigned char subTexturePackId;
bool isReadyToJoin; // 1 byte
bool isReadyToJoin;
bool isJoinable;
char hostIP[64];
int hostPort;
wchar_t hostName[XUSER_NAME_SIZE];
unsigned char playerCount;
unsigned char maxPlayers;
_GameSessionData()
{
@@ -76,6 +83,13 @@ typedef struct _GameSessionData
m_uiGameHostSettings = 0;
texturePackParentId = 0;
subTexturePackId = 0;
isReadyToJoin = false;
isJoinable = true;
memset(hostIP, 0, sizeof(hostIP));
hostPort = 0;
memset(hostName, 0, sizeof(hostName));
playerCount = 0;
maxPlayers = MINECRAFT_NET_MAX_PLAYERS;
}
} GameSessionData;
#endif
+19 -6
View File
@@ -691,7 +691,7 @@ void UIController::tickInput()
#endif
{
#ifdef _WINDOWS64
if (!g_KBMInput.IsMouseGrabbed())
if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
{
UIScene *pScene = NULL;
for (int grp = 0; grp < eUIGroup_COUNT && !pScene; ++grp)
@@ -1018,7 +1018,7 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key)
released = InputManager.ButtonReleased(iPad,key); // Toggle
#ifdef _WINDOWS64
if (iPad == 0)
if (iPad == 0 && g_KBMInput.IsKBMActive())
{
int vk = 0;
switch (key)
@@ -1029,10 +1029,10 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key)
case ACTION_MENU_DOWN: vk = VK_DOWN; break;
case ACTION_MENU_LEFT: vk = VK_LEFT; break;
case ACTION_MENU_RIGHT: vk = VK_RIGHT; break;
case ACTION_MENU_X: vk = 'E'; break;
case ACTION_MENU_X: vk = 'R'; break;
case ACTION_MENU_Y: vk = VK_TAB; break;
case ACTION_MENU_LEFT_SCROLL: vk = 'Q'; break;
case ACTION_MENU_RIGHT_SCROLL: vk = 'R'; break;
case ACTION_MENU_RIGHT_SCROLL: vk = 'E'; break;
case ACTION_MENU_PAGEUP: vk = VK_PRIOR; break;
case ACTION_MENU_PAGEDOWN: vk = VK_NEXT; break;
}
@@ -1043,7 +1043,20 @@ void UIController::handleKeyPress(unsigned int iPad, unsigned int key)
if (!pressed && !released && g_KBMInput.IsKeyDown(vk)) { down = true; }
}
if ((key == ACTION_MENU_OK || key == ACTION_MENU_A) && !g_KBMInput.IsMouseGrabbed())
if ((key == ACTION_MENU_UP || key == ACTION_MENU_DOWN) && !pressed && !released && !down)
{
bool inCrafting = (m_groups[(EUIGroup)(iPad+1)]->FindScene(eUIScene_Crafting2x2Menu) != NULL)
|| (m_groups[(EUIGroup)(iPad+1)]->FindScene(eUIScene_Crafting3x3Menu) != NULL);
if (inCrafting)
{
int wsKey = (key == ACTION_MENU_UP) ? 'W' : 'S';
if (g_KBMInput.IsKeyPressed(wsKey)) { pressed = true; down = true; }
if (g_KBMInput.IsKeyReleased(wsKey)) { released = true; down = false; }
if (!pressed && !released && g_KBMInput.IsKeyDown(wsKey)) { down = true; }
}
}
if ((key == ACTION_MENU_OK || key == ACTION_MENU_A) && !g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
{
if (g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT)) { pressed = true; down = true; }
if (g_KBMInput.IsMouseButtonReleased(KeyboardMouseInput::MOUSE_LEFT)) { released = true; down = false; }
@@ -1477,7 +1490,7 @@ GDrawTexture * RADLINK UIController::TextureSubstitutionCreateCallback ( void *
// 4J Stu - All our flash controls that allow replacing textures use a special 64x64 symbol
// Force this size here so that our images don't get scaled wildly
#if (defined __ORBIS__ || defined _DURANGO )
#if (defined __ORBIS__ || defined _DURANGO || defined _WINDOWS64 )
*width = 96;
*height = 96;
#else
+1 -1
View File
@@ -1164,7 +1164,7 @@ void UIScene::externalCallback(IggyExternalFunctionCallUTF16 * call)
void UIScene::registerSubstitutionTexture(const wstring &textureName, PBYTE pbData, DWORD dwLength, bool deleteData)
{
m_registeredTextures[textureName] = deleteData;;
m_registeredTextures[textureName] = deleteData;
ui.registerSubstitutionTexture(textureName, pbData, dwLength);
}
@@ -454,6 +454,11 @@ SCreditTextItemDef UIScene_Credits::gs_aCreditDefs[MAX_CREDIT_STRINGS] =
{ L"Victoria Bruder (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText },
#elif defined(_WIN64)
{ L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
{ L"Code Used", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText },
{ L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
{ L"LCEMP Multiplayer", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText },
{ L"notpies", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText },
#elif defined(__PSVITA__)
// font credits
{ L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line
+4 -1
View File
@@ -6,6 +6,7 @@
#define PSVITA_CREDITS_COUNT 77
#define PS4_CREDITS_COUNT 75
#define XBOXONE_CREDITS_COUNT (75+318)
#define WIN64_EXTRA_CREDITS_COUNT 5
#define MILES_AND_IGGY_CREDITS_COUNT 8
#define DYNAMODE_FONT_CREDITS_COUNT 2
#define PS3_DOLBY_CREDIT 4
@@ -15,8 +16,10 @@
#define MAX_CREDIT_STRINGS (PS3_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT + DYNAMODE_FONT_CREDITS_COUNT + PS3_DOLBY_CREDIT)
#elif defined(__ORBIS__)
#define MAX_CREDIT_STRINGS (PS4_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT + DYNAMODE_FONT_CREDITS_COUNT)
#elif defined(_DURANGO) || defined _WIN64
#elif defined(_DURANGO)
#define MAX_CREDIT_STRINGS (XBOXONE_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT)
#elif defined _WIN64
#define MAX_CREDIT_STRINGS (XBOXONE_CREDITS_COUNT + WIN64_EXTRA_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT)
#elif defined(__PSVITA__)
#define MAX_CREDIT_STRINGS (PSVITA_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT + DYNAMODE_FONT_CREDITS_COUNT)
#endif
@@ -15,6 +15,8 @@
// Windows64 DLC store stuff blahhhh - whisper
#ifdef _WINDOWS64
#include "Windows64_DLCOffers.h"
#include "..\..\User.h"
#include "Windows64_Minecraft.h"
#include <unordered_map>
static std::unordered_map<int,int> s_offerIndexToListPos;
#endif
@@ -166,10 +168,12 @@ void UIScene_DLCOffersMenu::handleInput(int iPad, int key, bool repeat, bool pre
int iIndex = getControlChildFocus();
if (iIndex >= 0 && iIndex < Windows64_DLCOffers::Get().GetOfferCount())
{
Minecraft *pMinecraft = Minecraft::GetInstance();
Windows64_DLCOffers::Get().InstallOffer(iIndex,
[](const wchar_t* id, bool ok, void*) {
wprintf(L"[DLC] Install %ls: %ls\n", id, ok ? L"OK" : L"FAILED");
}, nullptr);
}, nullptr, pMinecraft->user->name.c_str());
}
}
#endif
@@ -249,9 +253,9 @@ void UIScene_DLCOffersMenu::handlePress(F64 controlId, F64 childId)
iIndex < Windows64_DLCOffers::Get().GetOfferCount())
{
Windows64_DLCOffers::Get().InstallOffer(iIndex,
[](const wchar_t* id, bool ok, void*) {
wprintf(L"[DLC] Install %ls: %ls\n", id, ok ? L"OK" : L"FAILED");
}, nullptr);
[](const wchar_t* id, bool ok, void*) {
wprintf(L"[DLC] Install %ls: %ls\n", id, ok ? L"OK" : L"FAILED");
}, nullptr, Minecraft::GetInstance()->user->name.c_str());
}
#else
@@ -191,6 +191,8 @@ UIScene_LoadMenu::UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLaye
else
{
#if defined(__PS3__) || defined(__ORBIS__)|| defined(_DURANGO) || defined (__PSVITA__)
// convert to utf16
uint16_t u16Message[MAX_SAVEFILENAME_LENGTH];
@@ -2,11 +2,6 @@
#include "UI.h"
#include "UIScene_LoadOrJoinMenu.h"
#ifdef _WINDOWS64
#include "..\Network\P2PConnectionManager.h"
#include "..\Network\GameNetworkManager.h"
#endif
#include "..\..\..\Minecraft.World\StringHelpers.h"
#include "..\..\..\Minecraft.World\net.minecraft.world.item.h"
#include "..\..\..\Minecraft.World\net.minecraft.world.level.h"
@@ -288,13 +283,8 @@ UIScene_LoadOrJoinMenu::~UIScene_LoadOrJoinMenu()
g_NetworkManager.SetSessionsUpdatedCallback( NULL, NULL );
app.SetLiveLinkRequired( false );
if(m_currentSessions)
{
for(AUTO_VAR(it, m_currentSessions->begin()); it < m_currentSessions->end(); ++it)
{
delete (*it);
}
}
delete m_currentSessions;
m_currentSessions = NULL;
#if TO_BE_IMPLEMENTED
// Reset the background downloading, in case we changed it by attempting to download a texture pack
@@ -1739,24 +1729,6 @@ void UIScene_LoadOrJoinMenu::UpdateGamesList()
}
}
#ifdef _WINDOWS64
if (g_NetworkManager.IsP2PActive() && g_NetworkManager.IsInGameplay())
{
int directPeers = g_NetworkManager.GetP2PManager()->GetDirectPeerCount();
int totalOnline = g_NetworkManager.GetOnlinePlayerCount();
if (directPeers > 0 && totalOnline > 1)
{
wchar_t labelWithP2P[256];
swprintf_s(labelWithP2P, 256, L"%s [P2P:%d/%d]", sessionInfo->displayLabel, directPeers, totalOnline - 1);
m_buttonListGames.addItem(labelWithP2P, textureName);
}
else
{
m_buttonListGames.addItem(sessionInfo->displayLabel, textureName);
}
}
else
#endif
m_buttonListGames.addItem( sessionInfo->displayLabel, textureName );
if(memcmp( &selectedSessionId, &sessionInfo->sessionId, sizeof(SessionID) ) == 0)
@@ -105,7 +105,6 @@ private:
bool m_bSaveTransferCancelled;
#endif
bool m_bUpdateSaveSize;
wstring m_directConnectAddress;
public:
UIScene_LoadOrJoinMenu(int iPad, void *initData, UILayer *parentLayer);
@@ -154,7 +153,6 @@ public:
static int DeleteSaveDataReturned(LPVOID lpParam,bool bRes);
static int RenameSaveDataReturned(LPVOID lpParam,bool bRes);
static int KeyboardCompleteWorldNameCallback(LPVOID lpParam,bool bRes);
static int KeyboardCompleteDirectConnectCallback(LPVOID lpParam,bool bRes);
protected:
void handlePress(F64 controlId, F64 childId);
void LoadLevelGen(LevelGenerationOptions *levelGen);
@@ -9,6 +9,7 @@
#ifdef _WINDOWS64
#include "AchievementScreen.h"
#include "StatsCounter.h"
#include "Windows64_Minecraft.h"
#endif
#ifdef __ORBIS__
#include <error_dialog.h>
@@ -48,7 +49,7 @@ UIScene_MainMenu::UIScene_MainMenu(int iPad, void *initData, UILayer *parentLaye
if(ProfileManager.IsFullVersion())
{
m_bTrialVersion=false;
m_buttons[(int)eControl_UnlockOrDLC].init(app.GetString(IDS_DOWNLOADABLECONTENT),eControl_UnlockOrDLC);
m_buttons[(int)eControl_UnlockOrDLC].init(L"LCE Workshop",eControl_UnlockOrDLC);
}
else
{
@@ -115,6 +116,8 @@ UIScene_MainMenu::UIScene_MainMenu(int iPad, void *initData, UILayer *parentLaye
// Fix for #45154 - Frontend: DLC: Content can only be downloaded from the frontend if you have not joined/exited multiplayer
XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW);
#endif
Minecraft::GetInstance()->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad()));
}
UIScene_MainMenu::~UIScene_MainMenu()
@@ -179,7 +182,7 @@ void UIScene_MainMenu::handleGainFocus(bool navBack)
if(navBack && ProfileManager.IsFullVersion())
{
// Replace the Unlock Full Game with Downloadable Content
m_buttons[(int)eControl_UnlockOrDLC].setLabel(app.GetString(IDS_DOWNLOADABLECONTENT));
m_buttons[(int)eControl_UnlockOrDLC].setLabel(L"LCE Workshop");
}
#if TO_BE_IMPLEMENTED
@@ -368,6 +371,12 @@ void UIScene_MainMenu::handlePress(F64 controlId, F64 childId)
break;
#endif
#ifdef _WINDOWS64
case eControl_Exit:
PostMessage(GetMinecraftWindowHWND(), WM_CLOSE, 0, 0);
break;
#endif
default: __debugbreak();
}
-5
View File
@@ -75,12 +75,7 @@ void EnderDragonRenderer::renderModel(shared_ptr<Entity> _mob, float wp, float w
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(1, 0, 0, 0.5f);
#ifdef __PSVITA__
// AP - not sure that the usecompiled flag is supposed to be false. This makes it really slow on vita. Making it true still seems to look the same
model->render(mob, wp, ws, bob, headRotMinusBodyRot, headRotx, scale, true);
#else
model->render(mob, wp, ws, bob, headRotMinusBodyRot, headRotx, scale, false);
#endif
glEnable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDepthFunc(GL_LEQUAL);
+219 -278
View File
@@ -1,4 +1,5 @@
#include "stdafx.h"
#include "Extrax64Stubs.h"
#ifndef __PS3__
//#include <compressapi.h>
#endif // __PS3__
@@ -40,6 +41,7 @@
#ifdef _WINDOWS64
#include "discord_game_sdk.h"
#pragma comment(lib, "discord_game_sdk.dll.lib")
#include "Windows64\Network\WinsockNetLayer.h"
#endif
#ifndef E_FAIL
@@ -72,6 +74,90 @@ VOID ATG::XMLParser::RegisterSAXCallbackInterface( ISAXCallback *pISAXCallback )
static char s_discordUsername[32] = "";
static ULONGLONG s_discordXuid = 0;
static struct IDiscordCore *discordCore = NULL;
static char s_DiscordJoinSecret[64] = "";
static IDiscordActivityEvents s_ActivityEvents;
void TickDiscord()
{
if(discordCore != NULL)
discordCore->run_callbacks(discordCore);
}
static char s_pendingJoinIP[64] = "";
static int s_pendingJoinPort = 0;
static bool s_hasPendingJoin = false;
static void OnActivityJoin(void* event_data, const char* join_secret)
{
printf("Discord OnActivityJoin: secret=%s\n", join_secret);
char secret[64];
strncpy_s(secret, sizeof(secret), join_secret, _TRUNCATE);
char* colon = strchr(secret, ':');
if (colon != NULL)
{
*colon = '\0';
strncpy_s(s_pendingJoinIP, sizeof(s_pendingJoinIP), secret, _TRUNCATE);
s_pendingJoinPort = atoi(colon + 1);
FriendSessionInfo info;
memset(&info, 0, sizeof(info));
strncpy_s(info.data.hostIP, sizeof(info.data.hostIP), s_pendingJoinIP, _TRUNCATE);
info.data.hostPort = s_pendingJoinPort;
info.data.isJoinable = true;
info.data.isReadyToJoin = true;
// Allocate displayLabel on heap so the destructor can safely free it
static const wchar_t* label = L"Discord Invite";
size_t labelLen = wcslen(label);
info.displayLabel = new wchar_t[labelLen + 1];
wcscpy_s(info.displayLabel, labelLen + 1, label);
info.displayLabelLength = (unsigned char)labelLen;
g_NetworkManager.s_pPlatformNetworkManager->JoinGame(&info, 1,0);
}
}
static void OnActivityInvite(void* event_data, enum EDiscordActivityActionType type, struct DiscordUser* user, struct DiscordActivity* activity)
{
printf("Discord OnActivityInvite from user: %s\n", user->username);
struct IDiscordActivityManager* am = discordCore->get_activity_manager(discordCore);
if (am != NULL)
{
static int s_dummy = 0;
am->accept_invite(am, user->id, &s_dummy,
[](void* data, enum EDiscordResult result)
{
printf("accept_invite result: %d\n", result);
});
}
}
static void OnActivityJoinRequest(void* event_data, struct DiscordUser* user)
{
printf("Discord join request from: %s\n", user->username);
struct IDiscordActivityManager* am = discordCore->get_activity_manager(discordCore);
if(am != NULL)
{
static int s_dummy = 0;
}
}
void Discord_SetJoinSecret(const char* hostIp, WORD port)
{
printf("Discord_SetJoinSecret called!\n");
sprintf_s(s_DiscordJoinSecret, sizeof(s_DiscordJoinSecret), "%s:%u", hostIp, (unsigned)port);
ProfileManager.SetCurrentGameActivity(0,4,false);
}
static void InitDiscordCore()
{
@@ -80,19 +166,31 @@ static void InitDiscordCore()
static IDiscordCoreEvents coreEvents;
memset(&coreEvents, 0, sizeof(coreEvents));
memset(&s_ActivityEvents, 0, sizeof(s_ActivityEvents));
s_ActivityEvents.on_activity_join = OnActivityJoin;
s_ActivityEvents.on_activity_invite = OnActivityInvite;
s_ActivityEvents.on_activity_join_request = OnActivityJoinRequest;
struct DiscordCreateParams params;
DiscordCreateParamsSetDefault(&params);
params.client_id = 1477855587978182828LL;
params.flags = DiscordCreateFlags_NoRequireDiscord;
params.events = &coreEvents;
params.event_data = NULL;
params.activity_events = &s_ActivityEvents;
enum EDiscordResult result = DiscordCreate(DISCORD_VERSION, &params, &discordCore);
if(result != DiscordResult_Ok)
{
printf("DiscordCreate failed: %d\n", result);
discordCore = NULL;
return;
}
struct IDiscordActivityManager* am = discordCore->get_activity_manager(discordCore);
if (am != NULL)
am->register_command(am, "Minecraft.exe");
}
static ULONGLONG DiscordIdToXuid(DiscordSnowflake discordId)
@@ -108,9 +206,6 @@ static void InitDiscordIdentity()
for(int i = 0; i < 100; ++i)
{
discordCore->run_callbacks(discordCore);
Sleep(10);
struct IDiscordUserManager *userManager = discordCore->get_user_manager(discordCore);
if(userManager != NULL)
{
@@ -256,7 +351,7 @@ void PIXSetMarkerDeprecated(int a, char *b, ...) {}
bool IsEqualXUID(PlayerUID a, PlayerUID b)
{
#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) || defined(_DURANGO)
#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) || defined(_DURANGO) || defined(_WINDOWS64)
return (a == b);
#else
return false;
@@ -280,33 +375,6 @@ namespace
char s_stubGamertagA[XUSER_MAX_COUNT][32];
wchar_t s_stubGamertagW[XUSER_MAX_COUNT][32];
DWORD StubSupportedLocalUserMask()
{
DWORD mask = 0;
for(unsigned int i = 0; i < XUSER_MAX_COUNT && i < 32; ++i)
{
mask |= (1u << i);
}
return mask;
}
bool StubSupportsLocalUser(DWORD userIndex)
{
return userIndex < XUSER_MAX_COUNT;
}
DWORD StubClampLocalUserMask(DWORD usersMask)
{
DWORD clampedMask = usersMask & StubSupportedLocalUserMask();
if(clampedMask == 0)
{
clampedMask = (1u << 0);
}
return clampedMask;
}
void EnsureStubIdentity()
{
if(s_stubIdentityInitialised)
@@ -347,11 +415,7 @@ namespace
}
else
{
#ifdef _WINDOWS64
sprintf_s(s_stubGamertagA[i], sizeof(s_stubGamertagA[i]), "Player%u-%u", pid % 10000, i);
#else
snprintf(s_stubGamertagA[i], sizeof(s_stubGamertagA[i]), "Player%u-%u", pid % 10000, i);
#endif
}
}
@@ -365,70 +429,36 @@ namespace
}
}
extern bool _bQNetStubIsHost;
BYTE IQNetPlayer::GetSmallId()
{
int idx = (int)(this - &IQNet::m_player[0]);
if(idx < 0 || idx >= (int)XUSER_MAX_COUNT)
{
return 0;
}
if(!_bQNetStubIsHost)
{
// When acting as a client, reserve small id 0 for the remote host.
return (BYTE)(idx + 1);
}
return (BYTE)idx;
}
QNET_STATE _iQNetStubState = QNET_STATE_IDLE;
BYTE IQNetPlayer::GetSmallId() { return m_smallId; }
void IQNetPlayer::SendData(IQNetPlayer *player, const void *pvData, DWORD dwDataSize, DWORD dwFlags)
{
app.DebugPrintf("Sending from 0x%x to 0x%x %d bytes\n",this,player,dwDataSize);
}
bool IQNetPlayer::IsSameSystem(IQNetPlayer *player)
{
if(player == NULL)
if (WinsockNetLayer::IsActive())
{
return false;
WinsockNetLayer::SendToSmallId(player->m_smallId, pvData, dwDataSize);
}
ULONG_PTR ptr = (ULONG_PTR)player;
ULONG_PTR begin = (ULONG_PTR)&IQNet::m_player[0];
ULONG_PTR end = (ULONG_PTR)&IQNet::m_player[XUSER_MAX_COUNT];
return (ptr >= begin && ptr < end);
}
bool IQNetPlayer::IsSameSystem(IQNetPlayer *player) { return (this == player) || (!m_isRemote && !player->m_isRemote); }
DWORD IQNetPlayer::GetSendQueueSize( IQNetPlayer *player, DWORD dwFlags ) { return 0; }
DWORD IQNetPlayer::GetCurrentRtt() { return 0; }
extern bool _bQNetStubIsHost;
bool IQNetPlayer::IsHost() { return _bQNetStubIsHost && this == &IQNet::m_player[0]; }
bool IQNetPlayer::IsHost() { return m_isHostPlayer; }
bool IQNetPlayer::IsGuest() { return false; }
bool IQNetPlayer::IsLocal() { return true; }
bool IQNetPlayer::IsLocal() { return !m_isRemote; }
PlayerUID IQNetPlayer::GetXuid()
{
EnsureStubIdentity();
int idx = (int)(this - &IQNet::m_player[0]);
if(idx < 0 || idx >= (int)XUSER_MAX_COUNT)
// For the local player (smallId 0 on host, or the assigned smallId on client),
// try Discord XUID first for richer identity
if (!m_isRemote)
{
idx = 0;
EnsureStubIdentity();
if (s_discordXuid != 0)
return s_discordXuid | 0x100000000ULL;
}
#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__)
PlayerUID uid;
uid.setUserID(0x70000000u | (unsigned int)(idx & 0xFF));
return uid;
#else
return (s_stubXuidBase + (ULONGLONG)(idx & 0x0F)) | 0x100000000ULL;
#endif
return (PlayerUID)(0xe000d45248242f2e + m_smallId);
}
LPCWSTR IQNetPlayer::GetGamertag()
{
EnsureStubIdentity();
int idx = (int)(this - &IQNet::m_player[0]);
if(idx < 0 || idx >= (int)XUSER_MAX_COUNT)
{
idx = 0;
}
return s_stubGamertagW[idx];
}
int IQNetPlayer::GetSessionIndex() { return GetSmallId(); }
LPCWSTR IQNetPlayer::GetGamertag() { return m_gamertag; }
int IQNetPlayer::GetSessionIndex() { return m_smallId; }
bool IQNetPlayer::IsTalking() { return false; }
bool IQNetPlayer::IsMutedByLocalUser(DWORD dwUserIndex) { return false; }
bool IQNetPlayer::HasVoice() { return false; }
@@ -441,156 +471,118 @@ ULONG_PTR IQNetPlayer::GetCustomDataValue() {
return m_customData;
}
IQNetPlayer IQNet::m_player[4];
IQNetPlayer IQNet::m_player[MINECRAFT_NET_MAX_PLAYERS];
DWORD IQNet::s_playerCount = 1;
bool IQNet::s_isHosting = true;
bool _bQNetStubGameRunning = false;
bool _bQNetStubIsHost = true;
DWORD _dwQNetStubLocalUsersMask = (1u << 0);
HRESULT IQNet::AddLocalPlayerByUserIndex(DWORD dwUserIndex)
void Win64_SetupRemoteQNetPlayer(IQNetPlayer *player, BYTE smallId, bool isHost, bool isLocal)
{
if(!StubSupportsLocalUser(dwUserIndex))
{
return E_FAIL;
}
_dwQNetStubLocalUsersMask |= (1u << dwUserIndex);
return S_OK;
player->m_smallId = smallId;
player->m_isRemote = !isLocal;
player->m_isHostPlayer = isHost;
swprintf_s(player->m_gamertag, 32, L"Player%d", smallId);
if (smallId >= IQNet::s_playerCount)
IQNet::s_playerCount = smallId + 1;
}
static bool Win64_IsActivePlayer(IQNetPlayer *p, DWORD index)
{
if (index == 0) return true;
return (p->GetCustomDataValue() != 0);
}
HRESULT IQNet::AddLocalPlayerByUserIndex(DWORD dwUserIndex){ return S_OK; }
IQNetPlayer *IQNet::GetHostPlayer() { return &m_player[0]; }
IQNetPlayer *IQNet::GetLocalPlayerByUserIndex(DWORD dwUserIndex)
{
if(!StubSupportsLocalUser(dwUserIndex))
if (s_isHosting)
{
if (dwUserIndex < MINECRAFT_NET_MAX_PLAYERS &&
!m_player[dwUserIndex].m_isRemote &&
Win64_IsActivePlayer(&m_player[dwUserIndex], dwUserIndex))
return &m_player[dwUserIndex];
return NULL;
}
return (_dwQNetStubLocalUsersMask & (1u << dwUserIndex)) ? &m_player[dwUserIndex] : NULL;
if (dwUserIndex != 0)
return NULL;
for (DWORD i = 0; i < s_playerCount; i++)
{
if (!m_player[i].m_isRemote && Win64_IsActivePlayer(&m_player[i], i))
return &m_player[i];
}
return NULL;
}
IQNetPlayer *IQNet::GetPlayerByIndex(DWORD dwPlayerIndex)
{
if(!_bQNetStubGameRunning)
DWORD found = 0;
for (DWORD i = 0; i < s_playerCount; i++)
{
return NULL;
}
DWORD currentIndex = 0;
for(DWORD localUser = 0; localUser < XUSER_MAX_COUNT; ++localUser)
{
if((_dwQNetStubLocalUsersMask & (1u << localUser)) == 0)
if (Win64_IsActivePlayer(&m_player[i], i))
{
continue;
if (found == dwPlayerIndex) return &m_player[i];
found++;
}
if(currentIndex == dwPlayerIndex)
{
return &m_player[localUser];
}
++currentIndex;
}
return NULL;
return &m_player[0];
}
IQNetPlayer *IQNet::GetPlayerBySmallId(BYTE SmallId)
{
if(!_bQNetStubGameRunning)
{
if (SmallId >= MINECRAFT_NET_MAX_PLAYERS)
return NULL;
}
for(DWORD localUser = 0; localUser < XUSER_MAX_COUNT; ++localUser)
{
if((_dwQNetStubLocalUsersMask & (1u << localUser)) == 0)
{
continue;
}
if(m_player[localUser].GetSmallId() == SmallId)
{
return &m_player[localUser];
}
}
return NULL;
m_player[SmallId].m_smallId = SmallId;
if (SmallId >= s_playerCount)
s_playerCount = SmallId + 1;
return &m_player[SmallId];
}
IQNetPlayer *IQNet::GetPlayerByXuid(PlayerUID xuid)
{
if(!_bQNetStubGameRunning)
for (DWORD i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++)
{
return NULL;
if (Win64_IsActivePlayer(&m_player[i], i) && m_player[i].GetXuid() == xuid) return &m_player[i];
}
for(DWORD localUser = 0; localUser < XUSER_MAX_COUNT; ++localUser)
{
if((_dwQNetStubLocalUsersMask & (1u << localUser)) == 0)
{
continue;
}
IQNetPlayer *localPlayer = &m_player[localUser];
PlayerUID localXuidOnline = localPlayer->GetXuid();
#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) || defined(_DURANGO)
if(xuid == localXuidOnline)
{
return localPlayer;
}
#else
PlayerUID localXuidOffline = localXuidOnline & ~0x100000000ULL;
if(xuid == localXuidOnline || xuid == localXuidOffline)
{
return localPlayer;
}
#endif
}
return NULL;
}
DWORD IQNet::GetPlayerCount()
{
if(!_bQNetStubGameRunning)
{
return 0;
}
DWORD count = 0;
for(DWORD localUser = 0; localUser < XUSER_MAX_COUNT; ++localUser)
for (DWORD i = 0; i < s_playerCount; i++)
{
if(_dwQNetStubLocalUsersMask & (1u << localUser))
{
++count;
}
if (Win64_IsActivePlayer(&m_player[i], i)) count++;
}
return count;
}
QNET_STATE IQNet::GetState() { return _bQNetStubGameRunning ? QNET_STATE_GAME_PLAY : QNET_STATE_IDLE; }
bool IQNet::IsHost() { return _bQNetStubIsHost; }
HRESULT IQNet::JoinGameFromInviteInfo(DWORD dwUserIndex, DWORD dwUserMask, const INVITE_INFO *pInviteInfo)
QNET_STATE IQNet::GetState() { return _iQNetStubState; }
bool IQNet::IsHost() { return s_isHosting; }
HRESULT IQNet::JoinGameFromInviteInfo(DWORD dwUserIndex, DWORD dwUserMask, const INVITE_INFO *pInviteInfo) { return S_OK; }
void IQNet::HostGame() { _iQNetStubState = QNET_STATE_SESSION_STARTING; s_isHosting = true; }
void IQNet::ClientJoinGame()
{
UNREFERENCED_PARAMETER(dwUserIndex);
UNREFERENCED_PARAMETER(pInviteInfo);
_bQNetStubIsHost = false;
_bQNetStubGameRunning = true;
_dwQNetStubLocalUsersMask = StubClampLocalUserMask(dwUserMask);
if(StubSupportsLocalUser(dwUserIndex))
_iQNetStubState = QNET_STATE_SESSION_STARTING;
s_isHosting = false;
for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++)
{
_dwQNetStubLocalUsersMask |= (1u << dwUserIndex);
m_player[i].m_smallId = (BYTE)i;
m_player[i].m_isRemote = true;
m_player[i].m_isHostPlayer = false;
m_player[i].m_gamertag[0] = 0;
m_player[i].SetCustomDataValue(0);
}
_dwQNetStubLocalUsersMask = StubClampLocalUserMask(_dwQNetStubLocalUsersMask);
return S_OK;
}
void IQNet::HostGame()
{
_bQNetStubIsHost = true;
_bQNetStubGameRunning = true;
_dwQNetStubLocalUsersMask = (1u << 0);
}
void IQNet::EndGame()
{
_bQNetStubGameRunning = false;
_bQNetStubIsHost = true;
_dwQNetStubLocalUsersMask = (1u << 0);
_iQNetStubState = QNET_STATE_IDLE;
s_isHosting = false;
s_playerCount = 1;
for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++)
{
m_player[i].m_smallId = (BYTE)i;
m_player[i].m_isRemote = false;
m_player[i].m_isHostPlayer = false;
m_player[i].m_gamertag[0] = 0;
m_player[i].SetCustomDataValue(0);
}
}
DWORD MinecraftDynamicConfigurations::GetTrialTime() { return DYNAMIC_CONFIG_DEFAULT_TRIAL_TIME; }
@@ -766,7 +758,6 @@ DWORD XEnableGuestSignin(BOOL fEnable) { return 0; }
#ifdef _WINDOWS64
static void *profileData[4];
static bool s_bProfileIsFullVersion;
static int s_iPrimaryPad = 0;
void C_4JProfile::Initialise( DWORD dwTitleID,
DWORD dwOfferID,
unsigned short usProfileVersion,
@@ -836,21 +827,8 @@ void C_4JProfile::SetTrialTextStringTable(CXuiStringTable *pStringTable,int i
void C_4JProfile::SetTrialAwardText(eAwardType AwardType,int iTitle,int iText) {}
int C_4JProfile::GetLockedProfile() { return 0; }
void C_4JProfile::SetLockedProfile(int iProf) {}
bool C_4JProfile::IsSignedIn(int iQuadrant)
{
if(iQuadrant < 0 || iQuadrant >= (int)XUSER_MAX_COUNT)
{
return false;
}
if(iQuadrant == 0)
{
return true;
}
return InputManager.IsPadConnected(iQuadrant);
}
bool C_4JProfile::IsSignedInLive(int iProf) { return IsSignedIn(iProf); }
bool C_4JProfile::IsSignedIn(int iQuadrant) { return ( iQuadrant == 0); }
bool C_4JProfile::IsSignedInLive(int iProf) { return true; }
bool C_4JProfile::IsGuest(int iQuadrant) { return false; }
UINT C_4JProfile::RequestSignInUI(bool bFromInvite,bool bLocalGame,bool bNoGuestsAllowed,bool bMultiplayerSignIn,bool bAddUser, int( *Func)(LPVOID,const bool, const int iPad),LPVOID lpParam,int iQuadrant) { return 0; }
UINT C_4JProfile::DisplayOfflineProfile(int( *Func)(LPVOID,const bool, const int iPad),LPVOID lpParam,int iQuadrant) { return 0; }
@@ -859,18 +837,23 @@ void C_4JProfile::SetPrimaryPlayerChanged(bool bVal) {}
bool C_4JProfile::QuerySigninStatus(void) { return true; }
void C_4JProfile::GetXUID(int iPad, PlayerUID *pXuid,bool bOnlineXuid)
{
EnsureStubIdentity();
if(pXuid == NULL)
{
return;
}
ULONGLONG value = s_stubXuidBase + (ULONGLONG)(iPad & 0x0F);
if(bOnlineXuid)
#ifdef _WINDOWS64
if (iPad != 0)
{
value |= 0x100000000ULL;
*pXuid = INVALID_XUID;
return;
}
*pXuid = value;
if (IQNet::s_isHosting)
*pXuid = 0xe000d45248242f2e;
else
*pXuid = 0xe000d45248242f2e + WinsockNetLayer::GetLocalSmallId();
#else
*pXuid = 0xe000d45248242f2e + iPad;
#endif
}
BOOL C_4JProfile::AreXUIDSEqual(PlayerUID xuid1,PlayerUID xuid2) { return xuid1 == xuid2; }
BOOL C_4JProfile::XUIDIsGuest(PlayerUID xuid) { return false; }
@@ -891,45 +874,17 @@ void C_4JProfile::AllowedPlayerCreatedContent(int iPad, bool thisQuadrantOnly
BOOL C_4JProfile::CanViewPlayerCreatedContent(int iPad, bool thisQuadrantOnly, PPlayerUID pXuids, DWORD dwXuidCount ) { return true; }
bool C_4JProfile::GetProfileAvatar(int iPad,int( *Func)(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes), LPVOID lpParam) { return false; }
void C_4JProfile::CancelProfileAvatarRequest() {}
int C_4JProfile::GetPrimaryPad() { return s_iPrimaryPad; }
void C_4JProfile::SetPrimaryPad(int iPad)
{
if(iPad >= 0 && iPad < (int)XUSER_MAX_COUNT && IsSignedIn(iPad))
{
s_iPrimaryPad = iPad;
}
else
{
s_iPrimaryPad = 0;
}
}
int C_4JProfile::GetPrimaryPad() { return 0; }
void C_4JProfile::SetPrimaryPad(int iPad) {}
#ifdef _DURANGO
char fakeGamerTag[32] = "PlayerName";
void SetFakeGamertag(char *name){ strcpy_s(fakeGamerTag, name); }
char* C_4JProfile::GetGamertag(int iPad){ return fakeGamerTag; }
#else
char* C_4JProfile::GetGamertag(int iPad)
{
EnsureStubIdentity();
if(iPad < 0 || iPad >= (int)XUSER_MAX_COUNT)
{
iPad = 0;
}
return s_stubGamertagA[iPad];
}
wstring C_4JProfile::GetDisplayName(int iPad)
{
EnsureStubIdentity();
if(iPad < 0 || iPad >= (int)XUSER_MAX_COUNT)
{
iPad = 0;
}
wchar_t displayName[32];
swprintf(displayName, 32, L"%S", s_stubGamertagA[iPad]);
return displayName;
}
char* C_4JProfile::GetGamertag(int iPad){ extern char g_Win64Username[17]; return g_Win64Username; }
wstring C_4JProfile::GetDisplayName(int iPad){ extern wchar_t g_Win64UsernameW[17]; return g_Win64UsernameW; }
#endif
bool C_4JProfile::IsFullVersion() { return true; }
bool C_4JProfile::IsFullVersion() { return s_bProfileIsFullVersion; }
void C_4JProfile::SetSignInChangeCallback(void ( *Func)(LPVOID, bool, unsigned int),LPVOID lpParam) {}
void C_4JProfile::SetNotificationsCallback(void ( *Func)(LPVOID, DWORD, unsigned int),LPVOID lpParam) {}
bool C_4JProfile::RegionIsNorthAmerica(void) { return false; }
@@ -950,10 +905,6 @@ int C_4JProfile::SetOldProfileVersionCallback(int( *Func)(LPVOID,unsigned ch
// To store the dashboard preferences for controller flipped, etc.
C_4JProfile::PROFILESETTINGS ProfileSettingsA[XUSER_MAX_COUNT];
#define MAX_AWARDS 32
static bool s_awardsUnlocked[XUSER_MAX_COUNT][MAX_AWARDS] = {};
C_4JProfile::PROFILESETTINGS * C_4JProfile::GetDashboardProfileSettings(int iPad) { return &ProfileSettingsA[iPad]; }
void C_4JProfile::WriteToProfile(int iQuadrant, bool bGameDefinedDataChanged, bool bOverride5MinuteLimitOnProfileWrites) {}
void C_4JProfile::ForceQueuedProfileWrites(int iPad) {}
@@ -979,24 +930,9 @@ void C_4JProfile::RegisterAward(int iAwardNumber,int iGamerconfigID, eAwardTy
CXuiStringTable*pStringTable, int iTitleStr, int iTextStr, int iAcceptStr, char *pszThemeName, unsigned int ulThemeSize) {}
int C_4JProfile::GetAwardId(int iAwardNumber) { return 0; }
eAwardType C_4JProfile::GetAwardType(int iAwardNumber) { return eAwardType_Achievement; }
bool C_4JProfile::CanBeAwarded(int iQuadrant, int iAwardNumber)
{
if (iQuadrant < 0 || iQuadrant >= XUSER_MAX_COUNT) return false;
if (iAwardNumber < 0 || iAwardNumber >= MAX_AWARDS) return false;
return !s_awardsUnlocked[iQuadrant][iAwardNumber];
}
void C_4JProfile::Award(int iQuadrant, int iAwardNumber, bool bForce)
{
if (iQuadrant < 0 || iQuadrant >= XUSER_MAX_COUNT) return;
if (iAwardNumber < 0 || iAwardNumber >= MAX_AWARDS) return;
s_awardsUnlocked[iQuadrant][iAwardNumber] = true;
}
bool C_4JProfile::IsAwardsFlagSet(int iQuadrant, int iAward)
{
if (iQuadrant < 0 || iQuadrant >= XUSER_MAX_COUNT) return false;
if (iAward < 0 || iAward >= MAX_AWARDS) return false;
return s_awardsUnlocked[iQuadrant][iAward];
}
bool C_4JProfile::CanBeAwarded(int iQuadrant, int iAwardNumber) { return false; }
void C_4JProfile::Award(int iQuadrant, int iAwardNumber, bool bForce) {}
bool C_4JProfile::IsAwardsFlagSet(int iQuadrant, int iAward) { return false; }
void C_4JProfile::RichPresenceInit(int iPresenceCount, int iContextCount) {}
void C_4JProfile::RegisterRichPresenceContext(int iGameConfigContextID) {}
void C_4JProfile::SetRichPresenceContextValue(int iPad,int iContextID, int iVal) {}
@@ -1030,6 +966,17 @@ void C_4JProfile::SetCurrentGameActivity(int iPad,int iNewPresence, bool bSet
case 4: // In-game Online
strncpy_s(activity.details, sizeof(activity.details), "Playing Minecraft", _TRUNCATE);
strncpy_s(activity.state, sizeof(activity.state), "Multiplayer", _TRUNCATE);
activity.instance = true;
sprintf_s(activity.party.id, sizeof(activity.party.id), "lce-whisper-session%lu", GetCurrentProcessId());
activity.party.size.current_size = (int32_t)g_NetworkManager.GetPlayerCount();
activity.party.size.max_size = (int32_t)MINECRAFT_NET_MAX_PLAYERS;
if (g_NetworkManager.IsHost() && s_DiscordJoinSecret[0] != '\0')
{
strncpy_s(activity.secrets.join, sizeof(activity.secrets.join), s_DiscordJoinSecret, _TRUNCATE);
}
break;
case 5: // In-game Singleplayer
strncpy_s(activity.details, sizeof(activity.details), "Playing Minecraft", _TRUNCATE);
@@ -1052,12 +999,6 @@ void C_4JProfile::SetCurrentGameActivity(int iPad,int iNewPresence, bool bSet
[](void *data, enum EDiscordResult result) {
printf("update_activity result: %d\n", result);
});
for(int i = 0; i < 50; ++i)
{
discordCore->run_callbacks(discordCore);
Sleep(10);
}
#endif
}
void C_4JProfile::DisplayFullVersionPurchase(bool bRequired, int iQuadrant, int iUpsellParam) {}
+3
View File
@@ -0,0 +1,3 @@
void Discord_SetJoinSecret(const char* hostIp, WORD port);
bool Discord_HasPendingJoin(char* outIP, int* outPort);
void TickDiscord();
+1 -1
View File
@@ -928,7 +928,7 @@ void GameRenderer::updateLightTexture(float a)
}
}
float brightness = 0.0f; // 4J - TODO - was mc->options->gamma;
float brightness = mc->options->gamma; // 4J - TODO - was mc->options->gamma;
if (_r > 1) _r = 1;
if (_g > 1) _g = 1;
if (_b > 1) _b = 1;
+4 -4
View File
@@ -46,7 +46,7 @@ void Input::tick(LocalPlayer *player)
float kbXA = 0.0f;
float kbYA = 0.0f;
#ifdef _WINDOWS64
if (iPad == 0 && g_KBMInput.IsMouseGrabbed())
if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
{
if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_RIGHT) )
kbXA = g_KBMInput.GetMoveX();
@@ -94,7 +94,7 @@ void Input::tick(LocalPlayer *player)
}
#ifdef _WINDOWS64
if (iPad == 0 && g_KBMInput.IsMouseGrabbed())
if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
{
// Left Shift = sneak (hold to crouch)
if (pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_SNEAK_TOGGLE))
@@ -166,7 +166,7 @@ void Input::tick(LocalPlayer *player)
float turnY = ty * abs(ty) * turnSpeed;
#ifdef _WINDOWS64
if (iPad == 0 && g_KBMInput.IsMouseGrabbed())
if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
{
float mouseSensitivity = ((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InGame)) / 100.0f;
float mouseLookScale = 5.0f;
@@ -190,7 +190,7 @@ void Input::tick(LocalPlayer *player)
unsigned int jump = InputManager.GetValue(iPad, MINECRAFT_ACTION_JUMP);
bool kbJump = false;
#ifdef _WINDOWS64
kbJump = (iPad == 0) && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_JUMP);
kbJump = (iPad == 0) && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive() && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_JUMP);
#endif
if( (jump > 0 || kbJump) && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_JUMP) )
jumping = true;
+2
View File
@@ -35,6 +35,8 @@ void KeyboardMouseInput::Init()
m_mouseGrabbed = false;
m_cursorHiddenForUI = false;
m_windowFocused = true;
m_kbmActive = true;
m_screenWantsCursorHidden = false;
m_hasInput = false;
RAWINPUTDEVICE rid;
+9
View File
@@ -64,6 +64,12 @@ public:
void SetWindowFocused(bool focused);
bool IsWindowFocused() const { return m_windowFocused; }
void SetKBMActive(bool active) { m_kbmActive = active; }
bool IsKBMActive() const { return m_kbmActive; }
void SetScreenCursorHidden(bool hidden) { m_screenWantsCursorHidden = hidden; }
bool IsScreenCursorHidden() const { return m_screenWantsCursorHidden; }
bool HasAnyInput() const { return m_hasInput; }
float GetMoveX() const;
@@ -106,6 +112,9 @@ private:
bool m_windowFocused;
bool m_kbmActive;
bool m_screenWantsCursorHidden;
bool m_hasInput;
};
+5
View File
@@ -1941,7 +1941,12 @@ bool LevelRenderer::updateDirtyChunks()
{
if( (!onlyRebuild) ||
globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_COMPILED ||
#ifdef _WINDOWS64
( distSq < 96 * 96 ) ) // Always rebuild really near things or else building (say) at tower up into empty blocks when we are low on memory will not create render data
#else
( distSq < 20 * 20 ) ) // Always rebuild really near things or else building (say) at tower up into empty blocks when we are low on memory will not create render data
#endif
{
considered++;
// Is this chunk nearer than our nearest?
+2
View File
@@ -49,6 +49,8 @@ public:
static const int MAX_COMMANDBUFFER_ALLOCATIONS = 448 * 1024 * 1024; // 4J - added - hard limit is 512 so giving a lot of headroom here for fragmentation (have seen 16MB lost to fragmentation in multiplayer crash dump before)
#elif defined __PS3__
static const int MAX_COMMANDBUFFER_ALLOCATIONS = 110 * 1024 * 1024; // 4J - added
#elif defined _WINDOWS64
static const int MAX_COMMANDBUFFER_ALLOCATIONS = 2047 * 1024 * 1024;
#else
static const int MAX_COMMANDBUFFER_ALLOCATIONS = 55 * 1024 * 1024; // 4J - added
#endif
+10 -1
View File
@@ -331,6 +331,12 @@ void LocalPlayer::aiStep()
}
}
if (isSneaking()) sprintTriggerTime = 0;
if (input->sprinting && !isSprinting() && enoughFoodToSprint && !isUsingItem() && !hasEffect(MobEffect::blindness) && input->ya >= runTreshold)
{
setSprinting(true);
}
// 4J-PB - try not stopping sprint on collision
//if (isSprinting() && (input->ya < runTreshold || horizontalCollision || !enoughFoodToSprint))
if (isSprinting() && (input->ya < runTreshold || !enoughFoodToSprint))
@@ -480,7 +486,10 @@ void LocalPlayer::aiStep()
// Check if the player is idle and the rich presence needs updated
if( !m_bIsIdle && InputManager.GetIdleSeconds( m_iPad ) > PLAYER_IDLE_TIME )
{
ProfileManager.SetCurrentGameActivity(m_iPad,CONTEXT_PRESENCE_IDLE,false);
#ifdef _WINDOWS64
if (!g_NetworkManager.s_pPlatformNetworkManager->IsInSession())
#endif
ProfileManager.SetCurrentGameActivity(m_iPad, CONTEXT_PRESENCE_IDLE, false);
m_bIsIdle = true;
}
else if ( m_bIsIdle && InputManager.GetIdleSeconds( m_iPad ) < PLAYER_IDLE_TIME )
+26 -38
View File
@@ -1297,7 +1297,7 @@ if not exist "$(TargetDir)\savedata" mkdir "$(TargetDir)\savedata"</Command>
<AdditionalDependencies>d3d11.lib;..\Minecraft.World\x64_Debug\Minecraft.World.lib;%(AdditionalDependencies);XInput9_1_0.lib;..\Minecraft.Client\Windows64\Miles\Lib\mss64.lib;discord_game_sdk.dll.lib</AdditionalDependencies>
<ShowProgress>NotSet</ShowProgress>
<SuppressStartupBanner>false</SuppressStartupBanner>
<AdditionalLibraryDirectories>$(ProjectDir)\discord;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<ImageXex>
<ConfigurationFile>$(ProjectDir)xbox\xex-dev.xml</ConfigurationFile>
@@ -1403,7 +1403,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<Optimization>Full</Optimization>
@@ -1411,9 +1411,9 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<BufferSecurityCheck>false</BufferSecurityCheck>
<PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PreprocessorDefinitions>_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<CallAttributedProfiling>Disabled</CallAttributedProfiling>
<AdditionalIncludeDirectories>Windows64\Iggy\include;$(ProjectDir);$(ProjectDir)\discord;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>Windows64\Iggy\include;$(ProjectDir);$(ProjectDir)\discord;$(ProjectDir)miniupnpc\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeTypeInfo>true</RuntimeTypeInfo>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
@@ -1423,10 +1423,10 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>$(OutDir)$(ProjectName).pdb</ProgramDatabaseFile>
<AdditionalDependencies>d3d11.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;discord_game_sdk.dll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>d3d11.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;discord_game_sdk.dll.lib;IPHlpApi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ShowProgress>NotSet</ShowProgress>
<SuppressStartupBanner>false</SuppressStartupBanner>
<AdditionalLibraryDirectories>$(ProjectDir)\discord;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(ProjectDir)\discord;C:\Program Files (x86)\Windows Kits\8.0\Lib\win8\um\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<ImageXex>
<ConfigurationFile>$(ProjectDir)xbox\xex-dev.xml</ConfigurationFile>
@@ -2938,6 +2938,8 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ORBIS'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|ORBIS'">true</ExcludedFromBuild>
</None>
<None Include="miniupnpc\genminiupnpcstrings.vbs" />
<None Include="miniupnpc\miniupnpcstrings.h.in" />
<None Include="Network Implementation Notes.txt" />
<None Include="Orbis\4JLibs\Libs\4J_Input.a">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|Durango'">true</ExcludedFromBuild>
@@ -4363,8 +4365,10 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<ClInclude Include="Common\Network\STUNClient.h" />
<ClInclude Include="ConnectScreen.h" />
<ClInclude Include="KeyboardMouseInput.h" />
<ClInclude Include="miniupnpc\miniupnpcstrings.h" />
<ClInclude Include="Windows64\Network\NetworkPlayerInterface.h" />
<ClInclude Include="Windows64\Network\P2PConnectionManagerWin.h" />
<ClInclude Include="Windows64\Network\WinsockNetLayer.h" />
<ClInclude Include="Common\Network\NetworkPlayerInterface.h" />
<ClInclude Include="Common\Network\PlatformNetworkManagerInterface.h" />
<ClInclude Include="Common\Network\PlatformNetworkManagerStub.h">
@@ -16609,37 +16613,6 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ORBIS'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|ORBIS'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="Windows64\Windows64_App.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|Durango'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|Durango'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseForArt|Durango'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|Durango'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugContentPackage|Durango'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|Durango'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Durango'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseForArt|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseForArt|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|ORBIS'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|ORBIS'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseForArt|ORBIS'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|ORBIS'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ORBIS'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ORBIS'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|ORBIS'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="Windows64\Windows64_UIController.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|Durango'">true</ExcludedFromBuild>
@@ -16710,9 +16683,11 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|ORBIS'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="Windows64_DLCOffers.h" />
<ClInclude Include="Windows64_Minecraft.h" />
<ClInclude Include="WolfModel.h" />
<ClInclude Include="WolfRenderer.h" />
<ClInclude Include="WstringLookup.h" />
<ClInclude Include="Extrax64Stubs.h" />
<ClInclude Include="Xbox\4JLibs\inc\4J_Input.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|x64'">true</ExcludedFromBuild>
@@ -21561,11 +21536,24 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<ClCompile Include="Common\Network\GameNetworkManager.cpp" />
<ClCompile Include="Common\Network\NATTraversal.cpp" />
<ClCompile Include="Common\Network\LANSessionManager.cpp" />
<ClCompile Include="Common\Network\PacketRouter.cpp" />
<ClCompile Include="Common\Network\STUNClient.cpp" />
<ClCompile Include="ConnectScreen.cpp" />
<ClCompile Include="KeyboardMouseInput.cpp" />
<ClCompile Include="miniupnpc\addr_is_reserved.c" />
<ClCompile Include="miniupnpc\connecthostport.c" />
<ClCompile Include="miniupnpc\igd_desc_parse.c" />
<ClCompile Include="miniupnpc\minisoap.c" />
<ClCompile Include="miniupnpc\minissdpc.c" />
<ClCompile Include="miniupnpc\miniupnpc.c" />
<ClCompile Include="miniupnpc\miniwget.c" />
<ClCompile Include="miniupnpc\minixml.c" />
<ClCompile Include="miniupnpc\portlistingparse.c" />
<ClCompile Include="miniupnpc\receivedata.c" />
<ClCompile Include="miniupnpc\upnpcommands.c" />
<ClCompile Include="miniupnpc\upnpdev.c" />
<ClCompile Include="miniupnpc\upnpreplyparse.c" />
<ClCompile Include="Windows64\Network\P2PConnectionManagerWin.cpp" />
<ClCompile Include="Windows64\Network\WinsockNetLayer.cpp" />
<ClCompile Include="Common\Network\PlatformNetworkManagerStub.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_NO_TU|Xbox 360'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|Xbox 360'">true</ExcludedFromBuild>
@@ -881,6 +881,8 @@
<None Include="PSVita\Iggy\gdraw\gdraw_shared.inl">
<Filter>PSVita\Iggy\gdraw</Filter>
</None>
<None Include="miniupnpc\miniupnpcstrings.h.in" />
<None Include="miniupnpc\genminiupnpcstrings.vbs" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
@@ -2338,9 +2340,6 @@
<ClInclude Include="Windows64\Resource.h">
<Filter>Windows64</Filter>
</ClInclude>
<ClInclude Include="Windows64\Windows64_App.h">
<Filter>Windows64</Filter>
</ClInclude>
<ClInclude Include="Durango\ApplicationView.h">
<Filter>Durango\Source Files</Filter>
</ClInclude>
@@ -2971,6 +2970,9 @@
<ClInclude Include="Windows64\Network\P2PConnectionManagerWin.h">
<Filter>Common\Source Files\Network</Filter>
</ClInclude>
<ClInclude Include="Windows64\Network\WinsockNetLayer.h">
<Filter>Common\Source Files\Network</Filter>
</ClInclude>
<ClInclude Include="PS3\PS3Extras\EdgeZLib.h">
<Filter>PS3\PS3Extras</Filter>
</ClInclude>
@@ -3658,6 +3660,15 @@
<ClInclude Include="KeyboardMouseInput.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Windows64_Minecraft.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="miniupnpc\miniupnpcstrings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Extrax64Stubs.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@@ -5085,15 +5096,15 @@
<ClCompile Include="Common\Network\LANSessionManager.cpp">
<Filter>Common\Source Files\Network</Filter>
</ClCompile>
<ClCompile Include="Common\Network\PacketRouter.cpp">
<Filter>Common\Source Files\Network</Filter>
</ClCompile>
<ClCompile Include="Common\Network\STUNClient.cpp">
<Filter>Common\Source Files\Network</Filter>
</ClCompile>
<ClCompile Include="Windows64\Network\P2PConnectionManagerWin.cpp">
<Filter>Common\Source Files\Network</Filter>
</ClCompile>
<ClCompile Include="Windows64\Network\WinsockNetLayer.cpp">
<Filter>Common\Source Files\Network</Filter>
</ClCompile>
<ClCompile Include="PS3\PS3Extras\EdgeZLib.cpp">
<Filter>PS3\PS3Extras</Filter>
</ClCompile>
@@ -5688,6 +5699,45 @@
<ClCompile Include="KeyboardMouseInput.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniupnpc\connecthostport.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniupnpc\igd_desc_parse.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniupnpc\minisoap.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniupnpc\minissdpc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniupnpc\miniupnpc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniupnpc\minixml.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniupnpc\portlistingparse.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniupnpc\receivedata.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniupnpc\upnpcommands.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniupnpc\upnpdev.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniupnpc\miniwget.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniupnpc\upnpreplyparse.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniupnpc\addr_is_reserved.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Library Include="Xbox\4JLibs\libs\4J_Render_d.lib">
+13 -12
View File
@@ -36,6 +36,7 @@
#include "Camera.h"
#ifdef _WINDOWS64
#include "KeyboardMouseInput.h"
#include "KeyMapping.h"
#endif
#include "..\Minecraft.World\MobEffect.h"
@@ -1470,7 +1471,7 @@ void Minecraft::run_middle()
{
if(InputManager.ButtonDown(i, MINECRAFT_ACTION_SNEAK_TOGGLE)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_SNEAK_TOGGLE;
#ifdef _WINDOWS64
if(i == 0 && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_SNEAK)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_SNEAK_TOGGLE;
if(i == 0 && g_KBMInput.IsKBMActive() && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_SNEAK)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_SNEAK_TOGGLE;
#endif
}
else
@@ -1481,7 +1482,7 @@ void Minecraft::run_middle()
if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_GAME_INFO)) localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_GAME_INFO;
#ifdef _WINDOWS64
if (i == 0)
if (i == 0 && g_KBMInput.IsKBMActive())
{
if(g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT))
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_ACTION;
@@ -1518,12 +1519,6 @@ void Minecraft::run_middle()
showFpsCounter = !showFpsCounter;
}
int wheel = g_KBMInput.GetMouseWheel();
if (wheel > 0)
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_RIGHT_SCROLL;
else if (wheel < 0)
localplayers[i]->ullButtonsPressed|=1LL<<MINECRAFT_ACTION_LEFT_SCROLL;
for (int slot = 0; slot < 9; slot++)
{
if (g_KBMInput.IsKeyPressed('1' + slot))
@@ -2339,7 +2334,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
if (screen == NULL && !ui.GetMenuDisplayed(iPad) )
{
#ifdef _WINDOWS64
if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsWindowFocused())
if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsWindowFocused() && g_KBMInput.IsKBMActive())
{
g_KBMInput.SetMouseGrabbed(true);
}
@@ -3285,7 +3280,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
wheel = -1;
}
#ifdef _WINDOWS64
if (iPad == 0 && wheel == 0)
if (iPad == 0 && wheel == 0 && g_KBMInput.IsKBMActive())
{
int mw = g_KBMInput.GetMouseWheel();
if (mw > 0) wheel = -1;
@@ -3325,7 +3320,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
}
#ifdef _WINDOWS64
bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION) || (iPad == 0 && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT));
bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION) || (iPad == 0 && g_KBMInput.IsKBMActive() && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT));
#else
bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION);
#endif
@@ -3356,7 +3351,7 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
}
*/
#ifdef _WINDOWS64
bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE) || (iPad == 0 && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_RIGHT));
bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE) || (iPad == 0 && g_KBMInput.IsKBMActive() && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_RIGHT));
#else
bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE);
#endif
@@ -3594,6 +3589,12 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
// }
// #endif
if(Keyboard::isKeyDown(options->keyChat->key))
{
setScreen(new ChatScreen());
}
#if 0
// 4J - TODO - some replacement for input handling...
if (screen == NULL || screen.passEvents)
@@ -161,12 +161,17 @@ bool CGameNetworkManager::_RunNetworkGame(LPVOID lpParameter)
success = s_pPlatformNetworkManager->_RunNetworkGame();
if(!success)
{
{
app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitWorld,(void *)TRUE);
return true;
}
}
else
{
// Client needs QNET_STATE_GAME_PLAY so that IsInGameplay() returns true
s_pPlatformNetworkManager->SetGamePlayState();
}
if( g_NetworkManager.IsLeavingGame() ) return false;
app.SetGameStarted(true);
@@ -1391,7 +1396,10 @@ void CGameNetworkManager::CreateSocket( INetworkPlayer *pNetworkPlayer, bool loc
Minecraft *pMinecraft = Minecraft::GetInstance();
Socket *socket = NULL;
shared_ptr<MultiplayerLocalPlayer> mpPlayer = pMinecraft->localplayers[pNetworkPlayer->GetUserIndex()];
shared_ptr<MultiplayerLocalPlayer> mpPlayer = nullptr;
int userIdx = pNetworkPlayer->GetUserIndex();
if (userIdx >= 0 && userIdx < XUSER_MAX_COUNT)
mpPlayer = pMinecraft->localplayers[userIdx];
if( localPlayer && mpPlayer != NULL && mpPlayer->connection != NULL)
{
// If we already have a MultiplayerLocalPlayer here then we are doing a session type change
@@ -71,6 +71,7 @@ public:
virtual void HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0) = 0;
virtual int JoinGame(FriendSessionInfo *searchResult, int dwLocalUsersMask, int dwPrimaryUserIndex ) = 0;
virtual void CancelJoinGame() {};
virtual void SetGamePlayState() {};
virtual bool SetLocalGame(bool isLocal) = 0;
virtual bool IsLocalGame() = 0;
virtual void SetPrivateGame(bool isPrivate) = 0;
@@ -313,6 +313,12 @@ void CPlatformNetworkManagerStub::HandleSignInChange()
return;
}
void CPlatformNetworkManagerStub::SetGamePlayState()
{
extern QNET_STATE _iQNetStubState;
_iQNetStubState = QNET_STATE_GAME_PLAY;
}
bool CPlatformNetworkManagerStub::_RunNetworkGame()
{
return true;
@@ -55,6 +55,7 @@ public:
virtual void HandleSignInChange();
virtual bool _RunNetworkGame();
virtual void SetGamePlayState();
private:
bool isSystemPrimaryPlayer(IQNetPlayer *pQNetPlayer);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,153 @@
#pragma once
#ifdef _WINDOWS64
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <vector>
#include "..\..\Common\Network\NetworkPlayerInterface.h"
#pragma comment(lib, "Ws2_32.lib")
#define WIN64_NET_DEFAULT_PORT 25565
#define WIN64_NET_MAX_CLIENTS 7
#define WIN64_NET_RECV_BUFFER_SIZE 65536
#define WIN64_NET_MAX_PACKET_SIZE (4 * 1024 * 1024)
#define WIN64_LAN_DISCOVERY_PORT 25566
#define WIN64_LAN_BROADCAST_MAGIC 0x4D434C4E
class Socket;
#pragma pack(push, 1)
struct Win64LANBroadcast
{
DWORD magic;
WORD netVersion;
WORD gamePort;
wchar_t hostName[32];
BYTE playerCount;
BYTE maxPlayers;
DWORD gameHostSettings;
DWORD texturePackParentId;
BYTE subTexturePackId;
BYTE isJoinable;
};
#pragma pack(pop)
struct Win64LANSession
{
char hostIP[64];
int hostPort;
wchar_t hostName[32];
unsigned short netVersion;
unsigned char playerCount;
unsigned char maxPlayers;
unsigned int gameHostSettings;
unsigned int texturePackParentId;
unsigned char subTexturePackId;
bool isJoinable;
DWORD lastSeenTick;
};
struct Win64RemoteConnection
{
SOCKET tcpSocket;
BYTE smallId;
HANDLE recvThread;
volatile bool active;
};
class WinsockNetLayer
{
public:
static bool s_upnpMapped;
static char s_externalIP[64];
static void CleanupUPnP(int port);
static bool Initialize();
static void Shutdown();
static bool HostGame(int port);
static bool JoinGame(const char *ip, int port);
static bool SendToSmallId(BYTE targetSmallId, const void *data, int dataSize);
static bool SendOnSocket(SOCKET sock, const void *data, int dataSize);
static bool IsHosting() { return s_isHost; }
static bool IsConnected() { return s_connected; }
static bool IsActive() { return s_active; }
static BYTE GetLocalSmallId() { return s_localSmallId; }
static BYTE GetHostSmallId() { return s_hostSmallId; }
static SOCKET GetSocketForSmallId(BYTE smallId);
static void HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsigned char *data, unsigned int dataSize);
static bool PopDisconnectedSmallId(BYTE *outSmallId);
static void PushFreeSmallId(BYTE smallId);
static void CloseConnectionBySmallId(BYTE smallId);
static bool StartAdvertising(int gamePort, const wchar_t *hostName, unsigned int gameSettings, unsigned int texPackId, unsigned char subTexId, unsigned short netVer);
static void StopAdvertising();
static void UpdateAdvertisePlayerCount(BYTE count);
static void UpdateAdvertiseJoinable(bool joinable);
static bool StartDiscovery();
static void StopDiscovery();
static std::vector<Win64LANSession> GetDiscoveredSessions();
static int GetHostPort() { return s_hostGamePort; }
private:
static DWORD WINAPI AcceptThreadProc(LPVOID param);
static DWORD WINAPI RecvThreadProc(LPVOID param);
static DWORD WINAPI ClientRecvThreadProc(LPVOID param);
static DWORD WINAPI AdvertiseThreadProc(LPVOID param);
static DWORD WINAPI DiscoveryThreadProc(LPVOID param);
static SOCKET s_listenSocket;
static SOCKET s_hostConnectionSocket;
static HANDLE s_acceptThread;
static HANDLE s_clientRecvThread;
static bool s_isHost;
static bool s_connected;
static bool s_active;
static bool s_initialized;
static BYTE s_localSmallId;
static BYTE s_hostSmallId;
static BYTE s_nextSmallId;
static CRITICAL_SECTION s_sendLock;
static CRITICAL_SECTION s_connectionsLock;
static std::vector<Win64RemoteConnection> s_connections;
static SOCKET s_advertiseSock;
static HANDLE s_advertiseThread;
static volatile bool s_advertising;
static Win64LANBroadcast s_advertiseData;
static CRITICAL_SECTION s_advertiseLock;
static int s_hostGamePort;
static SOCKET s_discoverySock;
static HANDLE s_discoveryThread;
static volatile bool s_discovering;
static CRITICAL_SECTION s_discoveryLock;
static std::vector<Win64LANSession> s_discoveredSessions;
static CRITICAL_SECTION s_disconnectLock;
static std::vector<BYTE> s_disconnectedSmallIds;
static CRITICAL_SECTION s_freeSmallIdLock;
static std::vector<BYTE> s_freeSmallIds;
};
extern bool g_Win64MultiplayerHost;
extern bool g_Win64MultiplayerJoin;
extern int g_Win64MultiplayerPort;
extern char g_Win64MultiplayerIP[256];
#endif
+4 -1
View File
@@ -26,6 +26,8 @@ void CConsoleMinecraftApp::StoreLaunchData()
}
void CConsoleMinecraftApp::ExitGame()
{
extern HWND g_hWnd;
PostMessage(g_hWnd, WM_CLOSE, 0, 0);
}
void CConsoleMinecraftApp::FatalLoadError()
{
@@ -55,7 +57,8 @@ void CConsoleMinecraftApp::TemporaryCreateGameStart()
Minecraft *pMinecraft=Minecraft::GetInstance();
app.ReleaseSaveThumbnail();
ProfileManager.SetLockedProfile(0);
pMinecraft->user->name = L"Windows";
extern wchar_t g_Win64UsernameW[17];
pMinecraft->user->name = g_Win64UsernameW;
app.ApplyGameSettingsChanged(0);
////////////////////////////////////////////////////////////////////////////////////////////// From CScene_MultiGameJoinLoad::OnInit
@@ -37,9 +37,13 @@
#include "Resource.h"
#include "..\..\Minecraft.World\compression.h"
#include "..\..\Minecraft.World\OldChunkStorage.h"
#include "Network\WinsockNetLayer.h"
#include "Extrax64Stubs.h"
#include "Xbox/resource.h"
#include "Windows64_Minecraft.h"
HINSTANCE hMyInst;
LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam);
char chGlobalText[256];
@@ -80,6 +84,9 @@ BOOL g_bWidescreen = TRUE;
int g_iScreenWidth = 1920;
int g_iScreenHeight = 1080;
char g_Win64Username[17] = {0};
wchar_t g_Win64UsernameW[17] = {0};
void DefineActions(void)
{
// The app needs to define the actions required, and the possible mappings for these
@@ -289,6 +296,11 @@ void MemSect(int sect)
HINSTANCE g_hInst = NULL;
HWND g_hWnd = NULL;
HWND GetMinecraftWindowHWND()
{
return g_hWnd;
}
static bool g_isFullscreen = false;
static RECT g_windowedRect = {};
static LONG g_windowedStyle = 0;
@@ -335,6 +347,52 @@ D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
ID3D11Device* g_pd3dDevice = NULL;
ID3D11DeviceContext* g_pImmediateContext = NULL;
IDXGISwapChain* g_pSwapChain = NULL;
static WORD g_originalGammaRamp[3][256];
static bool g_gammaRampSaved = false;
void Windows64_UpdateGamma(unsigned short usGamma)
{
if (!g_hWnd) return;
HDC hdc = GetDC(g_hWnd);
if (!hdc) return;
if (!g_gammaRampSaved)
{
GetDeviceGammaRamp(hdc, g_originalGammaRamp);
g_gammaRampSaved = true;
}
float gamma = (float)usGamma / 32768.0f;
if (gamma < 0.01f) gamma = 0.01f;
if (gamma > 1.0f) gamma = 1.0f;
float invGamma = 1.0f / (0.5f + gamma * 0.5f);
WORD ramp[3][256];
for (int i = 0; i < 256; i++)
{
float normalized = (float)i / 255.0f;
float corrected = powf(normalized, invGamma);
WORD val = (WORD)(corrected * 65535.0f + 0.5f);
ramp[0][i] = val;
ramp[1][i] = val;
ramp[2][i] = val;
}
SetDeviceGammaRamp(hdc, ramp);
ReleaseDC(g_hWnd, hdc);
}
void Windows64_RestoreGamma()
{
if (!g_gammaRampSaved || !g_hWnd) return;
HDC hdc = GetDC(g_hWnd);
if (!hdc) return;
SetDeviceGammaRamp(hdc, g_originalGammaRamp);
ReleaseDC(g_hWnd, hdc);
}
ID3D11RenderTargetView* g_pRenderTargetView = NULL;
ID3D11DepthStencilView* g_pDepthStencilView = NULL;
ID3D11Texture2D* g_pDepthStencilBuffer = NULL;
@@ -822,6 +880,9 @@ void Render()
//--------------------------------------------------------------------------------------
void CleanupDevice()
{
extern void Windows64_RestoreGamma();
Windows64_RestoreGamma();
if( g_pImmediateContext ) g_pImmediateContext->ClearState();
if( g_pRenderTargetView ) g_pRenderTargetView->Release();
@@ -870,8 +931,32 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
//g_iScreenWidth = 960;
//g_iScreenHeight = 544;
}
char cmdLineA[1024];
strncpy_s(cmdLineA, sizeof(cmdLineA), lpCmdLine, _TRUNCATE);
char *nameArg = strstr(cmdLineA, "-name ");
if (nameArg)
{
nameArg += 6;
while (*nameArg == ' ') nameArg++;
char nameBuf[17];
int n = 0;
while (nameArg[n] && nameArg[n] != ' ' && n < 16) { nameBuf[n] = nameArg[n]; n++; }
nameBuf[n] = 0;
strncpy_s(g_Win64Username, 17, nameBuf, _TRUNCATE);
}
}
if (g_Win64Username[0] == 0)
{
DWORD sz = 17;
if (!GetUserNameA(g_Win64Username, &sz))
strncpy_s(g_Win64Username, 17, "Player", _TRUNCATE);
g_Win64Username[16] = 0;
}
MultiByteToWideChar(CP_ACP, 0, g_Win64Username, -1, g_Win64UsernameW, 17);
// Initialize global strings
MyRegisterClass(hInstance);
@@ -1021,6 +1106,22 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
ProfileManager.SetNotificationsCallback(&CConsoleMinecraftApp::NotificationsCallback,(LPVOID)&app);
#endif
// Ensure the GameHDD save directory exists at runtime (the 4J_Storage lib expects it)
{
wchar_t exePath[MAX_PATH];
if (GetModuleFileNameW(NULL, exePath, MAX_PATH))
{
wstring exeDir(exePath);
size_t lastSlash = exeDir.find_last_of(L"\\/");
if (lastSlash != wstring::npos)
exeDir = exeDir.substr(0, lastSlash);
wstring gameHDDPath = exeDir + L"\\Windows64\\GameHDD";
CreateDirectoryW((exeDir + L"\\Windows64").c_str(), NULL);
CreateDirectoryW(gameHDDPath.c_str(), NULL);
}
}
// Set a callback for the default player options to be set - when there is no profile data for the player
ProfileManager.SetDefaultOptionsCallback(&CConsoleMinecraftApp::DefaultOptionsCallback,(LPVOID)&app);
#if 0
@@ -1036,7 +1137,17 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
// ProfileManager for XN_LIVE_INVITE_ACCEPTED for QNet.
g_NetworkManager.Initialise();
for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++)
{
IQNet::m_player[i].m_smallId = (BYTE)i;
IQNet::m_player[i].m_isRemote = false;
IQNet::m_player[i].m_isHostPlayer = (i == 0);
swprintf_s(IQNet::m_player[i].m_gamertag, 32, L"Player%d", i);
}
extern wchar_t g_Win64UsernameW[17];
wcscpy_s(IQNet::m_player[0].m_gamertag, 32, g_Win64UsernameW);
WinsockNetLayer::Initialize();
// 4J-PB moved further down
//app.InitGameSettings();
@@ -1135,6 +1246,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
// Intro loop ?
while(app.IntroRunning())
{
ProfileManager.Tick();
// Tick XUI
app.RunFrame();
@@ -1156,14 +1268,30 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
MSG msg = {0};
while( WM_QUIT != msg.message )
{
g_KBMInput.Tick();
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
if( msg.message == WM_QUIT ) break;
TranslateMessage( &msg );
DispatchMessage( &msg );
continue;
}
if( msg.message == WM_QUIT ) break;
g_KBMInput.Tick();
#ifdef _DEBUG
for( int vk = 0; vk < 256; vk++ )
{
if( g_KBMInput.IsKeyPressed(vk) )
{
char dbgBuf[64];
sprintf_s(dbgBuf, "INPUT: Key pressed vk=0x%02X\n", vk);
OutputDebugStringA(dbgBuf);
}
}
#endif
RenderManager.StartFrame();
#if 0
if(pMinecraft->soundEngine->isStreamingWavebankReady() &&
@@ -1186,6 +1314,26 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
PIXBeginNamedEvent(0,"Input manager tick");
InputManager.Tick();
PIXEndNamedEvent();
if (InputManager.IsPadConnected(0))
{
bool controllerActive = InputManager.ButtonPressed(0) ||
InputManager.GetJoypadStick_LX(0,false) != 0.0f || InputManager.GetJoypadStick_LY(0,false) != 0.0f ||
InputManager.GetJoypadStick_RX(0,false) != 0.0f || InputManager.GetJoypadStick_RY(0,false) != 0.0f ||
InputManager.GetJoypadLTrigger(0,false) != 0 || InputManager.GetJoypadRTrigger(0,false) != 0;
if (controllerActive && g_KBMInput.IsKBMActive())
{
g_KBMInput.SetKBMActive(false);
g_KBMInput.SetMouseGrabbed(false);
g_KBMInput.SetCursorHiddenForUI(true);
}
else if (!g_KBMInput.IsKBMActive() && g_KBMInput.HasAnyInput())
{
g_KBMInput.SetCursorHiddenForUI(false);
g_KBMInput.SetKBMActive(true);
}
}
PIXBeginNamedEvent(0,"Profile manager tick");
// ProfileManager.Tick();
PIXEndNamedEvent();
@@ -1209,9 +1357,11 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
PIXEndNamedEvent();
PIXBeginNamedEvent(0,"Network manager do work #1");
// g_NetworkManager.DoWork();
g_NetworkManager.DoWork();
PIXEndNamedEvent();
TickDiscord();
// LeaderboardManager::Instance()->Tick();
// Render game graphics.
if(app.GetGameStarted())
+384 -358
View File
@@ -4,456 +4,482 @@
#include <winhttp.h>
#pragma comment(lib, "winhttp.lib")
#include <shlobj.h>
#include "../Windows64_Minecraft.h"
#include "../User.h"
static bool ParseUrl(const wchar_t* url,
std::wstring& host, INTERNET_PORT& port,
std::wstring& path)
std::wstring& host, INTERNET_PORT& port,
std::wstring& path)
{
URL_COMPONENTS uc = {};
uc.dwStructSize = sizeof(uc);
uc.dwHostNameLength = (DWORD)-1;
uc.dwUrlPathLength = (DWORD)-1;
uc.dwExtraInfoLength = (DWORD)-1;
if (!WinHttpCrackUrl(url, 0, 0, &uc)) return false;
host.assign(uc.lpszHostName, uc.dwHostNameLength);
path.assign(uc.lpszUrlPath, uc.dwUrlPathLength);
if (uc.lpszExtraInfo && uc.dwExtraInfoLength)
path.append(uc.lpszExtraInfo, uc.dwExtraInfoLength);
port = uc.nPort;
return true;
URL_COMPONENTS uc = {};
uc.dwStructSize = sizeof(uc);
uc.dwHostNameLength = (DWORD)-1;
uc.dwUrlPathLength = (DWORD)-1;
uc.dwExtraInfoLength = (DWORD)-1;
if (!WinHttpCrackUrl(url, 0, 0, &uc)) return false;
host.assign(uc.lpszHostName, uc.dwHostNameLength);
path.assign(uc.lpszUrlPath, uc.dwUrlPathLength);
if (uc.lpszExtraInfo && uc.dwExtraInfoLength)
path.append(uc.lpszExtraInfo, uc.dwExtraInfoLength);
port = uc.nPort;
return true;
}
bool Windows64_DLCOffers::FetchBytesFromUrl(const wchar_t* url,
PBYTE* ppData, DWORD* pdwSize)
PBYTE* ppData, DWORD* pdwSize)
{
*ppData = nullptr;
*pdwSize = 0;
*ppData = nullptr;
*pdwSize = 0;
printf("[DLC] FetchBytesFromUrl: '%ls'\n", url);
printf("[DLC] FetchBytesFromUrl: '%ls'\n", url);
std::wstring host, path;
INTERNET_PORT port = 80;
if (!ParseUrl(url, host, port, path))
{
printf("[DLC] FetchBytesFromUrl: ParseUrl FAILED (err=%u)\n", GetLastError());
return false;
}
std::wstring host, path;
INTERNET_PORT port = 80;
if (!ParseUrl(url, host, port, path))
{
printf("[DLC] FetchBytesFromUrl: ParseUrl FAILED (err=%u)\n", GetLastError());
return false;
}
printf("[DLC] FetchBytesFromUrl: host='%ls' port=%u path='%ls'\n",
host.c_str(), (unsigned)port, path.c_str());
printf("[DLC] FetchBytesFromUrl: host='%ls' port=%u path='%ls'\n",
host.c_str(), (unsigned)port, path.c_str());
HINTERNET hSession = WinHttpOpen(L"W64DLC/1.0",
WINHTTP_ACCESS_TYPE_NO_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession)
{
printf("[DLC] FetchBytesFromUrl: WinHttpOpen FAILED (err=%u)\n", GetLastError());
return false;
}
HINTERNET hSession = WinHttpOpen(L"W64DLC/1.0",
WINHTTP_ACCESS_TYPE_NO_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession)
{
printf("[DLC] FetchBytesFromUrl: WinHttpOpen FAILED (err=%u)\n", GetLastError());
return false;
}
HINTERNET hConn = WinHttpConnect(hSession, host.c_str(), port, 0);
if (!hConn)
{
printf("[DLC] FetchBytesFromUrl: WinHttpConnect FAILED (err=%u)\n", GetLastError());
WinHttpCloseHandle(hSession);
return false;
}
HINTERNET hConn = WinHttpConnect(hSession, host.c_str(), port, 0);
if (!hConn)
{
printf("[DLC] FetchBytesFromUrl: WinHttpConnect FAILED (err=%u)\n", GetLastError());
WinHttpCloseHandle(hSession);
return false;
}
HINTERNET hReq = WinHttpOpenRequest(hConn, L"GET", path.c_str(),
nullptr, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
if (!hReq)
{
printf("[DLC] FetchBytesFromUrl: WinHttpOpenRequest FAILED (err=%u)\n", GetLastError());
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSession);
return false;
}
HINTERNET hReq = WinHttpOpenRequest(hConn, L"GET", path.c_str(),
nullptr, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
if (!hReq)
{
printf("[DLC] FetchBytesFromUrl: WinHttpOpenRequest FAILED (err=%u)\n", GetLastError());
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSession);
return false;
}
if (!WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
{
printf("[DLC] FetchBytesFromUrl: WinHttpSendRequest FAILED (err=%u)\n", GetLastError());
WinHttpCloseHandle(hReq);
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSession);
return false;
}
if (!WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
{
printf("[DLC] FetchBytesFromUrl: WinHttpSendRequest FAILED (err=%u)\n", GetLastError());
WinHttpCloseHandle(hReq);
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSession);
return false;
}
if (!WinHttpReceiveResponse(hReq, nullptr))
{
printf("[DLC] FetchBytesFromUrl: WinHttpReceiveResponse FAILED (err=%u)\n", GetLastError());
WinHttpCloseHandle(hReq);
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSession);
return false;
}
if (!WinHttpReceiveResponse(hReq, nullptr))
{
printf("[DLC] FetchBytesFromUrl: WinHttpReceiveResponse FAILED (err=%u)\n", GetLastError());
WinHttpCloseHandle(hReq);
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSession);
return false;
}
DWORD dwStatusCode = 0;
DWORD dwStatusSize = sizeof(dwStatusCode);
WinHttpQueryHeaders(hReq,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
&dwStatusCode, &dwStatusSize,
WINHTTP_NO_HEADER_INDEX);
printf("[DLC] FetchBytesFromUrl: HTTP status = %u\n", dwStatusCode);
DWORD dwStatusCode = 0;
DWORD dwStatusSize = sizeof(dwStatusCode);
WinHttpQueryHeaders(hReq,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
&dwStatusCode, &dwStatusSize,
WINHTTP_NO_HEADER_INDEX);
printf("[DLC] FetchBytesFromUrl: HTTP status = %u\n", dwStatusCode);
if (dwStatusCode != 200)
{
printf("[DLC] FetchBytesFromUrl: non-200 response, aborting\n");
WinHttpCloseHandle(hReq);
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSession);
return false;
}
if (dwStatusCode != 200)
{
printf("[DLC] FetchBytesFromUrl: non-200 response, aborting\n");
WinHttpCloseHandle(hReq);
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSession);
return false;
}
std::vector<BYTE> buf;
DWORD dwRead = 0;
BYTE chunk[8192];
while (WinHttpReadData(hReq, chunk, sizeof(chunk), &dwRead) && dwRead)
buf.insert(buf.end(), chunk, chunk + dwRead);
std::vector<BYTE> buf;
DWORD dwRead = 0;
BYTE chunk[8192];
while (WinHttpReadData(hReq, chunk, sizeof(chunk), &dwRead) && dwRead)
buf.insert(buf.end(), chunk, chunk + dwRead);
printf("[DLC] FetchBytesFromUrl: read %zu bytes\n", buf.size());
printf("[DLC] FetchBytesFromUrl: read %zu bytes\n", buf.size());
bool bOk = false;
if (!buf.empty())
{
*ppData = new BYTE[buf.size()];
*pdwSize = (DWORD)buf.size();
memcpy(*ppData, buf.data(), buf.size());
bOk = true;
}
else
{
printf("[DLC] FetchBytesFromUrl: response body was empty\n");
}
bool bOk = false;
if (!buf.empty())
{
*ppData = new BYTE[buf.size()];
*pdwSize = (DWORD)buf.size();
memcpy(*ppData, buf.data(), buf.size());
bOk = true;
}
else
{
printf("[DLC] FetchBytesFromUrl: response body was empty\n");
}
WinHttpCloseHandle(hReq);
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSession);
return bOk;
WinHttpCloseHandle(hReq);
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSession);
return bOk;
}
static bool WinHttpGetUrl(const wchar_t* fullUrl, PBYTE* ppData, DWORD* pdwSize)
{
*ppData = nullptr;
*pdwSize = 0;
*ppData = nullptr;
*pdwSize = 0;
URL_COMPONENTS uc;
ZeroMemory(&uc, sizeof(uc));
uc.dwStructSize = sizeof(uc);
URL_COMPONENTS uc;
ZeroMemory(&uc, sizeof(uc));
uc.dwStructSize = sizeof(uc);
wchar_t szHostName[256] = {};
wchar_t szUrlPath[1024] = {};
uc.lpszHostName = szHostName;
uc.dwHostNameLength = _countof(szHostName);
uc.lpszUrlPath = szUrlPath;
uc.dwUrlPathLength = _countof(szUrlPath);
wchar_t szHostName[256] = {};
wchar_t szUrlPath[1024] = {};
uc.lpszHostName = szHostName;
uc.dwHostNameLength = _countof(szHostName);
uc.lpszUrlPath = szUrlPath;
uc.dwUrlPathLength = _countof(szUrlPath);
if (!WinHttpCrackUrl(fullUrl, 0, 0, &uc))
{
printf("[DLC] WinHttpGetUrl: WinHttpCrackUrl failed for '%ls': %u\n", fullUrl, GetLastError());
return false;
}
if (!WinHttpCrackUrl(fullUrl, 0, 0, &uc))
{
printf("[DLC] WinHttpGetUrl: WinHttpCrackUrl failed for '%ls': %u\n", fullUrl, GetLastError());
return false;
}
HINTERNET hSession = WinHttpOpen(L"MC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
if (!hSession) return false;
HINTERNET hSession = WinHttpOpen(L"MC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
if (!hSession) return false;
HINTERNET hConnect = WinHttpConnect(hSession, szHostName, uc.nPort, 0);
if (!hConnect) { WinHttpCloseHandle(hSession); return false; }
HINTERNET hConnect = WinHttpConnect(hSession, szHostName, uc.nPort, 0);
if (!hConnect) { WinHttpCloseHandle(hSession); return false; }
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", szUrlPath,
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
if (!hRequest)
{
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", szUrlPath,
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
if (!hRequest)
{
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) ||
!WinHttpReceiveResponse(hRequest, NULL))
{
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) ||
!WinHttpReceiveResponse(hRequest, NULL))
{
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return false;
}
std::string body;
DWORD dwSize = 0;
do {
DWORD dwDownloaded = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize) || dwSize == 0) break;
char* buf = new char[dwSize];
if (WinHttpReadData(hRequest, buf, dwSize, &dwDownloaded))
body.append(buf, dwDownloaded);
delete[] buf;
} while (dwSize > 0);
std::string body;
DWORD dwSize = 0;
do {
DWORD dwDownloaded = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize) || dwSize == 0) break;
char* buf = new char[dwSize];
if (WinHttpReadData(hRequest, buf, dwSize, &dwDownloaded))
body.append(buf, dwDownloaded);
delete[] buf;
} while (dwSize > 0);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
if (body.empty()) return false;
if (body.empty()) return false;
*pdwSize = (DWORD)body.size();
*ppData = new BYTE[*pdwSize];
memcpy(*ppData, body.data(), *pdwSize);
return true;
*pdwSize = (DWORD)body.size();
*ppData = new BYTE[*pdwSize];
memcpy(*ppData, body.data(), *pdwSize);
return true;
}
bool Windows64_DLCOffers::FetchImageFromUrl(const wchar_t* url, PBYTE* ppData, DWORD* pdwSize)
{
return WinHttpGetUrl(url, ppData, pdwSize);
return WinHttpGetUrl(url, ppData, pdwSize);
}
void Windows64_DLCOffers::InstallOffer(int iIndex,
W64_INSTALL_CALLBACK pfnCallback,
void* pUserData)
W64_INSTALL_CALLBACK pfnCallback,
void* pUserData, wstring username)
{
if (iIndex < 0 || iIndex >= (int)m_offers.size()) return;
if (iIndex < 0 || iIndex >= (int)m_offers.size()) return;
InstallCtx* ctx = new InstallCtx();
ctx->offer = m_offers[iIndex];
ctx->offerIndex = iIndex;
ctx->pfnCallback = pfnCallback;
ctx->pUserData = pUserData;
InstallCtx* ctx = new InstallCtx();
ctx->offer = m_offers[iIndex];
ctx->offerIndex = iIndex;
ctx->pfnCallback = pfnCallback;
ctx->pUserData = pUserData;
ctx->username = username;
HANDLE hThread = CreateThread(nullptr, 0, InstallThreadProc, ctx, 0, nullptr);
if (hThread) CloseHandle(hThread);
else delete ctx;
HANDLE hThread = CreateThread(nullptr, 0, InstallThreadProc, ctx, 0, nullptr);
if (hThread) CloseHandle(hThread);
else delete ctx;
}
DWORD WINAPI Windows64_DLCOffers::InstallThreadProc(LPVOID lpParam)
{
InstallCtx* ctx = reinterpret_cast<InstallCtx*>(lpParam);
bool bSuccess = false;
InstallCtx* ctx = reinterpret_cast<InstallCtx*>(lpParam);
bool bSuccess = false;
// <exe dir>\Windows64Media\DLC\<productID>\folder.pck
wchar_t wszExeDir[MAX_PATH] = {};
GetModuleFileNameW(nullptr, wszExeDir, MAX_PATH);
wchar_t* pLastSlash = wcsrchr(wszExeDir, L'\\');
if (pLastSlash) *(pLastSlash + 1) = L'\0';
// <exe dir>\Windows64Media\DLC\<productID>\folder.pck
wchar_t wszExeDir[MAX_PATH] = {};
GetModuleFileNameW(nullptr, wszExeDir, MAX_PATH);
wchar_t* pLastSlash = wcsrchr(wszExeDir, L'\\');
if (pLastSlash) *(pLastSlash + 1) = L'\0';
wchar_t wszInstallDir[MAX_PATH];
_snwprintf_s(wszInstallDir, _countof(wszInstallDir), _TRUNCATE,
L"%lsWindows64Media\\DLC\\%ls",
wszExeDir, ctx->offer.wszProductID);
wchar_t wszInstallDir[MAX_PATH];
_snwprintf_s(wszInstallDir, _countof(wszInstallDir), _TRUNCATE,
L"%lsWindows64Media\\DLC\\%ls",
wszExeDir, ctx->offer.wszProductID);
CreateDirectoryW(wszInstallDir, nullptr);
CreateDirectoryW(wszInstallDir, nullptr);
wchar_t wszFilePath[MAX_PATH];
_snwprintf_s(wszFilePath, _countof(wszFilePath), _TRUNCATE,
L"%ls\\folder.pck", wszInstallDir);
wchar_t wszFilePath[MAX_PATH];
_snwprintf_s(wszFilePath, _countof(wszFilePath), _TRUNCATE,
L"%ls\\folder.pck", wszInstallDir);
wchar_t wszDownloadUrl[512];
_snwprintf_s(wszDownloadUrl, _countof(wszDownloadUrl), _TRUNCATE,
L"http://127.0.0.1:3000/download/%ls", ctx->offer.wszProductID);
wchar_t wszDownloadUrl[512];
_snwprintf_s(wszDownloadUrl, _countof(wszDownloadUrl), _TRUNCATE,
L"http://127.0.0.1:3000/download/%ls", ctx->offer.wszProductID);
PBYTE pData = nullptr;
DWORD dwBytes = 0;
PBYTE pData = nullptr;
DWORD dwBytes = 0;
if (FetchBytesFromUrl(wszDownloadUrl, &pData, &dwBytes) && pData)
{
HANDLE hFile = CreateFileW(wszFilePath, GENERIC_WRITE, 0, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile != INVALID_HANDLE_VALUE)
{
DWORD dwWritten = 0;
WriteFile(hFile, pData, dwBytes, &dwWritten, nullptr);
CloseHandle(hFile);
bSuccess = (dwWritten == dwBytes);
}
delete[] pData;
}
if (FetchBytesFromUrl(wszDownloadUrl, &pData, &dwBytes) && pData)
{
HANDLE hFile = CreateFileW(wszFilePath, GENERIC_WRITE, 0, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile != INVALID_HANDLE_VALUE)
{
DWORD dwWritten = 0;
WriteFile(hFile, pData, dwBytes, &dwWritten, nullptr);
CloseHandle(hFile);
bSuccess = (dwWritten == dwBytes);
}
delete[] pData;
}
if (bSuccess)
{
// Mark the offer as owned.
Windows64_DLCOffers::Get().SetPurchased(ctx->offerIndex);
if (bSuccess)
{
Windows64_DLCOffers::Get().SetPurchased(ctx->offerIndex);
Windows64_DLCOffers::Get().SetPendingLoadPath(ctx->offerIndex, wszFilePath);
InterlockedExchange(&Windows64_DLCOffers::Get().m_iLastInstalled, (LONG)ctx->offerIndex);
// Must be set BEFORE m_iLastInstalled is updated so the game thread always
Windows64_DLCOffers::Get().SetPendingLoadPath(ctx->offerIndex, wszFilePath);
InterlockedExchange(&Windows64_DLCOffers::Get().m_iLastInstalled,
(LONG)ctx->offerIndex);
// notify server so purchase persists across sessions
wchar_t wszPath[256];
_snwprintf_s(wszPath, _countof(wszPath), _TRUNCATE,
L"/purchase/%ls?user=%ls",
ctx->offer.wszProductID, ctx->username.c_str());
printf("[DLC] Installed '%ls' -> %ls\n",
ctx->offer.wszProductID, wszFilePath);
}
else
{
printf("[DLC] Install FAILED for '%ls'\n", ctx->offer.wszProductID);
}
HINTERNET hSession = WinHttpOpen(L"W64DLC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, nullptr, nullptr, 0);
if (hSession)
{
HINTERNET hConn = WinHttpConnect(hSession, L"127.0.0.1", 3000, 0);
if (hConn)
{
HINTERNET hReq = WinHttpOpenRequest(hConn, L"POST", wszPath, nullptr,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
if (hReq)
{
WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
WinHttpReceiveResponse(hReq, nullptr);
WinHttpCloseHandle(hReq);
}
WinHttpCloseHandle(hConn);
}
WinHttpCloseHandle(hSession);
}
}
else
{
printf("[DLC] Install FAILED for '%ls'\n", ctx->offer.wszProductID);
}
if (ctx->pfnCallback)
ctx->pfnCallback(ctx->offer.wszProductID, bSuccess, ctx->pUserData);
if (ctx->pfnCallback)
ctx->pfnCallback(ctx->offer.wszProductID, bSuccess, ctx->pUserData);
delete ctx;
return 0;
delete ctx;
return 0;
}
void Windows64_DLCOffers::FetchFromServer()
{
printf("[DLC] FetchFromServer called\n");
m_offers.clear();
m_bReady = false;
printf("[DLC] FetchFromServer called\n");
m_offers.clear();
m_bReady = false;
HINTERNET hSession = WinHttpOpen(L"MC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
if (!hSession) return;
HINTERNET hSession = WinHttpOpen(L"MC/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
if (!hSession) return;
HINTERNET hConnect = WinHttpConnect(hSession, L"localhost", 3000, 0);
if (!hConnect) { WinHttpCloseHandle(hSession); return; }
HINTERNET hConnect = WinHttpConnect(hSession, L"localhost", 3000, 0);
if (!hConnect) { WinHttpCloseHandle(hSession); return; }
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", L"/dlc",
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
if (!hRequest)
{
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return;
}
wchar_t wszDlcPath[128];
_snwprintf_s(wszDlcPath, _countof(wszDlcPath), _TRUNCATE, L"/dlc?user=%ls", Minecraft::GetInstance()->user->name.c_str());
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) ||
!WinHttpReceiveResponse(hRequest, NULL))
{
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return;
}
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", wszDlcPath,
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
if (!hRequest)
{
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return;
}
std::string body;
DWORD dwSize = 0;
do {
DWORD dwDownloaded = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize) || dwSize == 0) break;
char* buf = new char[dwSize + 1];
ZeroMemory(buf, dwSize + 1);
if (WinHttpReadData(hRequest, buf, dwSize, &dwDownloaded))
body.append(buf, dwDownloaded);
delete[] buf;
} while (dwSize > 0);
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) ||
!WinHttpReceiveResponse(hRequest, NULL))
{
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return;
}
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
std::string body;
DWORD dwSize = 0;
do {
DWORD dwDownloaded = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize) || dwSize == 0) break;
char* buf = new char[dwSize + 1];
ZeroMemory(buf, dwSize + 1);
if (WinHttpReadData(hRequest, buf, dwSize, &dwDownloaded))
body.append(buf, dwDownloaded);
delete[] buf;
} while (dwSize > 0);
if (body.empty()) { printf("[DLC] Empty /dlc response\n"); return; }
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
printf("[DLC] /dlc response (%d bytes): %s\n", (int)body.size(), body.c_str());
if (body.empty()) { printf("[DLC] Empty /dlc response\n"); return; }
auto toWide = [](const std::string& s, wchar_t* dst, int maxChars) {
MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, dst, maxChars);
};
printf("[DLC] /dlc response (%d bytes): %s\n", (int)body.size(), body.c_str());
size_t pos = 0;
while ((pos = body.find('{', pos)) != std::string::npos)
{
W64_OFFER_INFO offer;
ZeroMemory(&offer, sizeof(offer));
auto toWide = [](const std::string& s, wchar_t* dst, int maxChars) {
MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, dst, maxChars);
};
auto readField = [&](const char* key) -> std::string {
std::string search = std::string("\"") + key + "\"";
size_t k = body.find(search, pos);
if (k == std::string::npos) return "";
size_t colon = body.find(':', k);
if (colon == std::string::npos) return "";
size_t q1 = body.find('"', colon);
if (q1 == std::string::npos) return "";
size_t q2 = body.find('"', q1 + 1);
if (q2 == std::string::npos) return "";
return body.substr(q1 + 1, q2 - q1 - 1);
};
size_t pos = 0;
while ((pos = body.find('{', pos)) != std::string::npos)
{
W64_OFFER_INFO offer;
ZeroMemory(&offer, sizeof(offer));
auto readInt = [&](const char* key) -> int {
std::string search = std::string("\"") + key + "\"";
size_t k = body.find(search, pos);
if (k == std::string::npos) return 0;
size_t colon = body.find(':', k);
if (colon == std::string::npos) return 0;
size_t n = colon + 1;
while (n < body.size() && (body[n] == ' ' || body[n] == '\t')) n++;
return atoi(&body[n]);
};
auto readField = [&](const char* key) -> std::string {
std::string search = std::string("\"") + key + "\"";
size_t k = body.find(search, pos);
if (k == std::string::npos) return "";
size_t colon = body.find(':', k);
if (colon == std::string::npos) return "";
size_t q1 = body.find('"', colon);
if (q1 == std::string::npos) return "";
size_t q2 = body.find('"', q1 + 1);
if (q2 == std::string::npos) return "";
return body.substr(q1 + 1, q2 - q1 - 1);
};
toWide(readField("id"), offer.wszProductID, 64);
toWide(readField("name"), offer.wszOfferName, 128);
toWide(readField("description"), offer.wszSellText, 512);
toWide(readField("price"), offer.wszCurrencyPrice, 32);
toWide(readField("type"), offer.wszType, 32);
toWide(readField("bannerUrl"), offer.wszBannerUrl, 256);
offer.fUserHasPurchased = readInt("purchased");
offer.pbBannerData = nullptr;
offer.dwBannerBytes = 0;
auto readInt = [&](const char* key) -> int {
std::string search = std::string("\"") + key + "\"";
size_t k = body.find(search, pos);
if (k == std::string::npos) return 0;
size_t colon = body.find(':', k);
if (colon == std::string::npos) return 0;
size_t n = colon + 1;
while (n < body.size() && (body[n] == ' ' || body[n] == '\t')) n++;
return atoi(&body[n]);
};
if (offer.wszProductID[0] != L'\0')
{
printf("[DLC] Parsed: id='%ls' type='%ls' bannerUrl='%ls'\n",
offer.wszProductID, offer.wszType, offer.wszBannerUrl);
m_offers.push_back(offer);
}
toWide(readField("id"), offer.wszProductID, 64);
toWide(readField("name"), offer.wszOfferName, 128);
toWide(readField("description"), offer.wszSellText, 512);
toWide(readField("price"), offer.wszCurrencyPrice, 32);
toWide(readField("type"), offer.wszType, 32);
toWide(readField("bannerUrl"), offer.wszBannerUrl, 256);
offer.fUserHasPurchased = readInt("purchased");
offer.pbBannerData = nullptr;
offer.dwBannerBytes = 0;
pos = body.find('}', pos);
if (pos == std::string::npos) break;
pos++;
}
if (offer.wszProductID[0] != L'\0')
{
printf("[DLC] Parsed: id='%ls' type='%ls' bannerUrl='%ls'\n",
offer.wszProductID, offer.wszType, offer.wszBannerUrl);
m_offers.push_back(offer);
}
printf("[DLC] FetchFromServer complete - %d offers\n", (int)m_offers.size());
m_bReady = true;
pos = body.find('}', pos);
if (pos == std::string::npos) break;
pos++;
}
printf("[DLC] FetchFromServer complete - %d offers\n", (int)m_offers.size());
m_bReady = true;
}
void Windows64_DLCOffers::FetchBanners()
{
for (int i = 0; i < (int)m_offers.size(); i++)
{
W64_OFFER_INFO& offer = m_offers[i];
if (offer.wszBannerUrl[0] == L'\0') continue;
if (offer.pbBannerData != nullptr) continue;
for (int i = 0; i < (int)m_offers.size(); i++)
{
W64_OFFER_INFO& offer = m_offers[i];
if (offer.wszBannerUrl[0] == L'\0') continue;
if (offer.pbBannerData != nullptr) continue;
PBYTE pbData = nullptr;
DWORD dwBytes = 0;
PBYTE pbData = nullptr;
DWORD dwBytes = 0;
if (FetchImageFromUrl(offer.wszBannerUrl, &pbData, &dwBytes))
{
offer.pbBannerData = pbData;
offer.dwBannerBytes = dwBytes;
printf("[DLC] Banner fetched for '%ls' - %u bytes\n",
offer.wszProductID, dwBytes);
}
else
{
printf("[DLC] Banner fetch FAILED for '%ls' url='%ls'\n",
offer.wszProductID, offer.wszBannerUrl);
}
}
if (FetchImageFromUrl(offer.wszBannerUrl, &pbData, &dwBytes))
{
offer.pbBannerData = pbData;
offer.dwBannerBytes = dwBytes;
printf("[DLC] Banner fetched for '%ls' - %u bytes\n",
offer.wszProductID, dwBytes);
}
else
{
printf("[DLC] Banner fetch FAILED for '%ls' url='%ls'\n",
offer.wszProductID, offer.wszBannerUrl);
}
}
// Register all offers with the app now that banners are populated.
for (int i = 0; i < (int)m_offers.size(); i++)
{
const W64_OFFER_INFO& offer = m_offers[i];
// Register all offers with the app now that banners are populated.
for (int i = 0; i < (int)m_offers.size(); i++)
{
const W64_OFFER_INFO& offer = m_offers[i];
eDLCContentType eType = e_DLC_SkinPack;
if (wcscmp(offer.wszType, L"MashUp") == 0) eType = e_DLC_MashupPacks;
else if (wcscmp(offer.wszType, L"TexturePack") == 0) eType = e_DLC_TexturePacks;
else if (wcscmp(offer.wszType, L"SkinPack") == 0) eType = e_DLC_SkinPack;
eDLCContentType eType = e_DLC_SkinPack;
if (wcscmp(offer.wszType, L"MashUp") == 0) eType = e_DLC_MashupPacks;
else if (wcscmp(offer.wszType, L"TexturePack") == 0) eType = e_DLC_TexturePacks;
else if (wcscmp(offer.wszType, L"SkinPack") == 0) eType = e_DLC_SkinPack;
printf("[DLC] Registering W64 DLC: '%ls' type=%d\n",
offer.wszProductID, (int)eType);
printf("[DLC] Registering W64 DLC: '%ls' type=%d\n",
offer.wszProductID, (int)eType);
app.RegisterW64DLC(
eType,
offer.wszProductID, // key AND banner texture name
offer.wszProductID, // banner texture name (used as lookup key in UpdateDisplay)
0, // iConfig
(unsigned int)i, // uiSortIndex
offer.pbBannerData,
offer.dwBannerBytes
);
}
app.RegisterW64DLC(
eType,
offer.wszProductID, // key AND banner texture name
offer.wszProductID, // banner texture name (used as lookup key in UpdateDisplay)
0, // iConfig
(unsigned int)i, // uiSortIndex
offer.pbBannerData,
offer.dwBannerBytes
);
}
}
#endif
+2 -1
View File
@@ -67,7 +67,7 @@ public:
void InstallOffer(int iIndex,
W64_INSTALL_CALLBACK pfnCallback = nullptr,
void* pUserData = nullptr);
void* pUserData = nullptr, wstring username = L"");
static bool FetchImageFromUrl(const wchar_t* url, PBYTE* ppData, DWORD* pdwSize);
static bool FetchBytesFromUrl(const wchar_t* url, PBYTE* ppData, DWORD* pdwSize);
@@ -95,6 +95,7 @@ private:
int offerIndex;
W64_INSTALL_CALLBACK pfnCallback;
void* pUserData;
wstring username;
};
static DWORD WINAPI InstallThreadProc(LPVOID lpParam);
+3
View File
@@ -0,0 +1,3 @@
#include <Windows.h>
HWND GetMinecraftWindowHWND();
+318
View File
@@ -0,0 +1,318 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
project (miniupnpc
VERSION 2.3.3
DESCRIPTION "UPnP IGD client lightweight library"
HOMEPAGE_URL https://miniupnp.tuxfamily.org/
LANGUAGES C)
set (MINIUPNPC_API_VERSION 21)
option (UPNPC_BUILD_STATIC "Build static library" TRUE)
option (UPNPC_BUILD_SHARED "Build shared library" TRUE)
option (UPNPC_BUILD_TESTS "Build test executables" TRUE)
option (UPNPC_BUILD_SAMPLE "Build sample executables" TRUE)
option (NO_GETADDRINFO "Define NO_GETADDRINFO" FALSE)
option (UPNPC_NO_INSTALL "Disable installation" FALSE)
if (NOT UPNPC_BUILD_STATIC AND NOT UPNPC_BUILD_SHARED)
message (FATAL "Both shared and static libraries are disabled!")
endif ()
include(GNUInstallDirs)
# Interface library for compile definitions, flags and option
add_library(miniupnpc-private INTERFACE)
if (NO_GETADDRINFO)
target_compile_definitions(miniupnpc-private INTERFACE NO_GETADDRINFO)
endif ()
if (NOT WIN32)
target_compile_definitions(miniupnpc-private INTERFACE
MINIUPNPC_SET_SOCKET_TIMEOUT
MINIUPNPC_GET_SRC_ADDR
_BSD_SOURCE _DEFAULT_SOURCE)
if (NOT APPLE AND NOT CMAKE_SYSTEM_NAME MATCHES ".*BSD" AND NOT CMAKE_SYSTEM_NAME STREQUAL "SunOS")
# add_definitions (-D_POSIX_C_SOURCE=200112L)
target_compile_definitions(miniupnpc-private INTERFACE _XOPEN_SOURCE=600)
endif ()
if (CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
target_compile_definitions(miniupnpc-private INTERFACE _NETBSD_SOURCE)
endif ()
else ()
set (MINIUPNPC_TARGET_WINDOWS_VERSION "0x0501" CACHE STRING "Minimum target Windows version as hex string") # XP or higher for getnameinfo and friends
if (MINIUPNPC_TARGET_WINDOWS_VERSION)
target_compile_definitions(miniupnpc-private INTERFACE _WIN32_WINNT=${MINIUPNPC_TARGET_WINDOWS_VERSION})
endif ()
endif ()
if (APPLE)
target_compile_definitions(miniupnpc-private INTERFACE _DARWIN_C_SOURCE)
endif ()
# Set compiler specific build flags
if (CMAKE_COMPILER_IS_GNUCC AND NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
target_compile_options(miniupnpc-private INTERFACE -Wall)
endif ()
# Suppress noise warnings
if (MSVC)
target_compile_definitions(miniupnpc-private INTERFACE _CRT_SECURE_NO_WARNINGS _WINSOCK_DEPRECATED_NO_WARNINGS)
endif()
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/miniupnpcstrings.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/miniupnpcstrings.h)
target_include_directories(miniupnpc-private INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
set (MINIUPNPC_SOURCES
src/igd_desc_parse.c
src/miniupnpc.c
src/minixml.c
src/minisoap.c
src/minissdpc.c
src/miniwget.c
src/upnpcommands.c
src/upnpdev.c
src/upnpreplyparse.c
src/upnperrors.c
src/connecthostport.c
src/portlistingparse.c
src/receivedata.c
src/addr_is_reserved.c
${CMAKE_CURRENT_BINARY_DIR}/miniupnpcstrings.h
)
if (WIN32)
target_link_libraries(miniupnpc-private INTERFACE ws2_32 iphlpapi)
elseif (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
target_link_libraries(miniupnpc-private INTERFACE socket nsl resolv)
find_library (SOCKET_LIBRARY NAMES socket)
find_library (NSL_LIBRARY NAMES nsl)
find_library (RESOLV_LIBRARY NAMES resolv)
set (LDLIBS ${SOCKET_LIBRARY} ${NSL_LIBRARY} ${RESOLV_LIBRARY} ${LDLIBS})
elseif (HAIKU)
target_link_libraries(miniupnpc-private INTERFACE network)
find_library (SOCKET_LIBRARY NAMES network)
find_library (NSL_LIBRARY NAMES network)
find_library (RESOLV_LIBRARY NAMES network)
set (LDLIBS ${SOCKET_LIBRARY} ${NSL_LIBRARY} ${RESOLV_LIBRARY} ${LDLIBS})
endif ()
if (UPNPC_BUILD_STATIC)
add_library (libminiupnpc-static STATIC ${MINIUPNPC_SOURCES})
target_include_directories (libminiupnpc-static PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/miniupnpc>)
if (NOT UPNPC_BUILD_SHARED)
add_library (miniupnpc::miniupnpc ALIAS libminiupnpc-static)
endif()
set_target_properties (libminiupnpc-static PROPERTIES EXPORT_NAME miniupnpc)
if (WIN32 AND NOT MINGW)
set_target_properties (libminiupnpc-static PROPERTIES OUTPUT_NAME "libminiupnpc")
else()
set_target_properties (libminiupnpc-static PROPERTIES OUTPUT_NAME "miniupnpc")
endif()
target_link_libraries (libminiupnpc-static PRIVATE miniupnpc-private)
target_include_directories(libminiupnpc-static INTERFACE $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
target_compile_definitions(libminiupnpc-static PUBLIC MINIUPNP_STATICLIB)
if (NOT UPNPC_NO_INSTALL)
install (TARGETS miniupnpc-private EXPORT miniupnpc-private)
install (EXPORT miniupnpc-private
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc"
NAMESPACE miniupnpc::)
install (TARGETS libminiupnpc-static
EXPORT libminiupnpc-static
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
install (EXPORT libminiupnpc-static
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc"
NAMESPACE miniupnpc::)
endif()
if (UPNPC_BUILD_SAMPLE)
add_executable (upnpc-static src/upnpc.c)
target_link_libraries (upnpc-static PRIVATE libminiupnpc-static)
target_include_directories(upnpc-static PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
add_executable (upnp-listdevices-static src/listdevices.c)
target_link_libraries (upnp-listdevices-static PRIVATE libminiupnpc-static)
target_include_directories(upnp-listdevices-static PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
if (NOT UPNPC_NO_INSTALL)
install (TARGETS upnpc-static upnp-listdevices-static
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
endif ()
endif ()
if (UPNPC_BUILD_SHARED)
add_library (libminiupnpc-shared SHARED ${MINIUPNPC_SOURCES})
target_include_directories (libminiupnpc-shared PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/miniupnpc>)
add_library (miniupnpc::miniupnpc ALIAS libminiupnpc-shared)
set_target_properties (libminiupnpc-shared PROPERTIES EXPORT_NAME miniupnpc)
set_target_properties (libminiupnpc-shared PROPERTIES OUTPUT_NAME "miniupnpc")
set_target_properties (libminiupnpc-shared PROPERTIES VERSION ${PROJECT_VERSION})
set_target_properties (libminiupnpc-shared PROPERTIES SOVERSION ${MINIUPNPC_API_VERSION})
target_link_libraries (libminiupnpc-shared PRIVATE miniupnpc-private)
target_compile_definitions(libminiupnpc-shared PRIVATE MINIUPNP_EXPORTS)
target_include_directories(libminiupnpc-shared INTERFACE $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
if (WIN32)
target_link_libraries(libminiupnpc-shared INTERFACE ws2_32 iphlpapi)
endif()
if (NOT UPNPC_NO_INSTALL)
install (TARGETS libminiupnpc-shared
EXPORT libminiupnpc-shared
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
install (EXPORT libminiupnpc-shared
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc"
NAMESPACE miniupnpc::)
endif()
if (UPNPC_BUILD_SAMPLE)
add_executable (upnpc-shared src/upnpc.c)
target_link_libraries (upnpc-shared PRIVATE libminiupnpc-shared)
target_include_directories(upnpc-shared PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
add_executable (upnp-listdevices-shared src/listdevices.c)
target_link_libraries (upnp-listdevices-shared PRIVATE libminiupnpc-shared)
target_include_directories(upnp-listdevices-shared PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
if (NOT UPNPC_NO_INSTALL)
install (TARGETS upnpc-shared upnp-listdevices-shared
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
endif ()
endif ()
if (UPNPC_BUILD_TESTS)
add_library(miniupnpc-tests INTERFACE)
target_link_libraries(miniupnpc-tests INTERFACE miniupnpc-private)
target_compile_definitions(miniupnpc-tests INTERFACE MINIUPNP_STATICLIB)
add_executable (testminixml src/testminixml.c src/minixml.c src/igd_desc_parse.c)
target_include_directories (testminixml PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_link_libraries (testminixml PRIVATE miniupnpc-tests)
add_executable (minixmlvalid src/minixmlvalid.c src/minixml.c)
target_link_libraries (minixmlvalid PRIVATE miniupnpc-tests)
add_executable (testupnpreplyparse src/testupnpreplyparse.c
src/minixml.c src/upnpreplyparse.c)
target_include_directories (testupnpreplyparse PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_link_libraries (testupnpreplyparse PRIVATE miniupnpc-tests)
add_executable (testigddescparse src/testigddescparse.c
src/igd_desc_parse.c src/minixml.c src/miniupnpc.c src/miniwget.c src/minissdpc.c
src/upnpcommands.c src/upnpreplyparse.c src/minisoap.c src/connecthostport.c
src/portlistingparse.c src/receivedata.c src/addr_is_reserved.c
)
target_include_directories (testigddescparse PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_link_libraries (testigddescparse PRIVATE miniupnpc-tests)
add_executable (testminiwget src/testminiwget.c
src/miniwget.c src/miniupnpc.c src/minisoap.c src/upnpcommands.c src/minissdpc.c
src/upnpreplyparse.c src/minixml.c src/igd_desc_parse.c src/connecthostport.c
src/portlistingparse.c src/receivedata.c src/addr_is_reserved.c
)
target_include_directories (testminiwget PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_link_libraries (testminiwget PRIVATE miniupnpc-tests)
add_executable (testaddr_is_reserved src/testaddr_is_reserved.c
src/addr_is_reserved.c
)
target_link_libraries (testaddr_is_reserved PRIVATE miniupnpc-tests)
add_executable (testportlistingparse src/testportlistingparse.c
src/minixml.c src/portlistingparse.c)
target_include_directories (testportlistingparse PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_link_libraries (testportlistingparse PRIVATE miniupnpc-tests)
if (NOT WIN32)
add_executable (minihttptestserver src/minihttptestserver.c)
endif()
# set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} testminixml minixmlvalid testupnpreplyparse testigddescparse testminiwget)
include(CTest)
add_test(NAME validateminixml
COMMAND minixmlvalid)
add_test(NAME validateminiwget
COMMAND ${CMAKE_SOURCE_DIR}/testminiwget.sh)
if (NOT WIN32)
set_property(TEST validateminiwget
PROPERTY ENVIRONMENT
TESTSERVER=${CMAKE_BINARY_DIR}/minihttptestserver
TESTMINIWGET=${CMAKE_BINARY_DIR}/testminiwget)
endif()
add_test(NAME validateupnpreplyparse
COMMAND ${CMAKE_SOURCE_DIR}/testupnpreplyparse.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
set_property(TEST validateupnpreplyparse
PROPERTY ENVIRONMENT
TESTUPNPREPLYPARSE=${CMAKE_BINARY_DIR}/testupnpreplyparse)
add_test(NAME validateportlistingparse
COMMAND testportlistingparse)
add_test(NAME validateigddescparse1
COMMAND testigddescparse new_LiveBox_desc.xml new_LiveBox_desc.values
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testdesc)
add_test(NAME validateigddescparse2
COMMAND testigddescparse linksys_WAG200G_desc.xml linksys_WAG200G_desc.values
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testdesc)
add_test(NAME validateaddr_is_reserved
COMMAND testaddr_is_reserved)
endif ()
configure_file(miniupnpc.pc.in miniupnpc.pc @ONLY)
if (NOT UPNPC_NO_INSTALL)
install (FILES
include/miniupnpc.h
include/miniwget.h
include/upnpcommands.h
include/igd_desc_parse.h
include/upnpreplyparse.h
include/upnperrors.h
include/upnpdev.h
include/miniupnpctypes.h
include/portlistingparse.h
include/miniupnpc_declspec.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniupnpc
)
install(FILES miniupnpc-config.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/miniupnpc.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
)
install(FILES man3/miniupnpc.3
DESTINATION ${CMAKE_INSTALL_MANDIR}/man3
)
install(FILES external-ip.sh
TYPE BIN
)
endif()
# vim: ts=2:sw=2:expandtab
+862
View File
@@ -0,0 +1,862 @@
$Id: Changelog.txt,v 1.280 2025/05/25 21:56:47 nanard Exp $
miniUPnP client Changelog.
VERSION 2.3.3 : released 2025/05/27
2025/05/25:
define WIN32_LEAN_AND_MEAN
2025/03/29:
fix a bug in ssdpDiscoverDevices() that has been there since 2010/04/05
and the use of getaddrinfo()
more argument checks in UPNP_AddAnyPortMapping() and
UPNP_GetGenericPortMappingEntryExt()
2025/03/18:
Add UPNP_GetSpecificPortMappingEntryExt() and
UPNP_GetGenericPortMappingEntryExt() functions to support retrieving
arbitrary long descriptions.
Increments API_VERSION to 21
VERSION 2.3.2 : released 2025/03/05
2025/02/24:
remove STRTOUI from public headers
remove parseURL() from public headers
remove struct NameValue from upnpreplyparse.h and use flexible array member
Increments API_VERSION to 20
VERSION 2.3.1 : released 2025/02/23
2025/02/23:
allocate argument arrays on stack instead of heap in upnpcommand.c
2025/02/09:
doxygen documentation of the public headers
VERSION 2.3.0 : released 2025/01/11
2025/01/10:
Change simpleUPnPcommand() prototype to remove unused first argument
Increments API_VERSION to 19
2024/07/27:
Add #define for UPNP_GetValidIGD() return values
VERSION 2.2.8 : released 2024/06/09
2024/05/16:
IPv6: try site-local before link-local
2024/05/08:
upnpc.c: Add -f option to upnpc program (delete multiple port redirections)
UPNP_GetValidIGD(): distinguish between not connected and connected to a
"private" network (with a reserved IP address).
Increments API_VERSION to 18
VERSION 2.2.7 : released 2024/03/20
2024/01/15:
listdevices.c: exit with status code 1 if no UPNP device is found
2024/01/07:
upnpc.c: reformat Usage
VERSION 2.2.6 : released 2024/01/04
2024/01/04:
includes charset="utf-8" in Content-Type
fix memory allocation error in minissdpc.c
2023/06/15:
Make User-Agent compliant.
listdevices => upnp-listdevices
VERSION 2.2.5 : released 2023/06/12
2023/06/05:
GetListOfPortMappings NewStartPort 0 => 1
2023/05/30:
CheckPinholeWorking is optional
add 60x errors from UPnP Device Architecture
2023/01/04:
cmake: install binaries, man pages and external-ip.sh
VERSION 2.2.4 : released 2022/10/21
2022/02/20:
upnpc: use of @ to replace local lan address
2021/11/09:
python module : Allow to specify the root description url
VERSION 2.2.3 : released 2021/09/28
2021/08/13:
Change directory structure : include/ and src/ directories.
VERSION 2.2.2 : released 2021/03/03
2021/01/15:
miniupnpcmodule.c: throw an exception in UPnP_discover()
2020/12/30:
Fix usage of IP_MULTICAST_IF with struct ip_mreqn
VERSION 2.2.1 : released 2020/12/20
2020/11/30:
Add miniupnpc.rc for .dll description
VERSION 2.2.0 : released 2020/11/09
2020/09/24:
Check properly for reserved IP addresses
2020/09/23:
prevent infinite loop in upnpDiscover()
2020/02/16:
Add Haiku support
2019/10/22:
testminiwget.sh can use either "ip addr" or "ifconfig -a
2019/10/13:
fix UPNP_GetValidIGD() when several devices are found
which are reachable from != local address
2019/08/24:
Allow Remote Host on upnpc command line
fix error 708 description in strupnperror()
2019/04/05:
Fix memory leak in upnpreplyparse.c with NewPortListing element
2019/03/10:
connecthostport.c: Code simplification, error trace fix
2019/01/23:
set timeout for select() in connecthostport()
2018/10/31:
miniupnpcmodule.c: check return of WSAStartup()
2018/07/14:
Fix and improve MSVC project :
Add Dll configurations
improve genminiupnpcstrings.vbs
2018/06/18:
Fixes for windows 64-bits.
VERSION 2.1 : released 2018/05/07
2018/05/07:
CMake Modernize and cleanup CMakeLists.txt
Update MS Visual Studio projects
2018/04/30:
listdevices: show devices sorted by XML desc URL
2018/04/26:
Small fix in miniupnpcmodule.c (python module)
Support cross compiling in Makefile.mingw
2018/04/06:
Use SOCKET type instead of int (for Win64 compilation)
Increments API_VERSION to 17
2018/02/22:
Disable usage of MiniSSDPd when using -m option
2017/12/11:
Fix buffer over run in minixml.c
Fix uninitialized variable access in upnpreplyparse.c
2017/05/05:
Fix CVE-2017-8798 Thanks to tin/Team OSTStrom
2016/11/11:
check strlen before memcmp in XML parsing portlistingparse.c
fix build under SOLARIS and CYGWIN
2016/10/11:
Add python 3 compatibility to IGD test
VERSION 2.0 : released 2016/04/19
2016/01/24:
change miniwget to return HTTP status code
increments API_VERSION to 16
2016/01/22:
Improve UPNPIGD_IsConnected() to check if WAN address is not private.
parse HTTP response status line in miniwget.c
2015/10/26:
snprintf() overflow check. check overflow in simpleUPnPcommand2()
2015/10/25:
fix compilation with old macs
fix compilation with mingw32 (for Appveyor)
fix python module for python <= 2.3
2015/10/08:
Change sameport to localport
see https://github.com/miniupnp/miniupnp/pull/120
increments API_VERSION to 15
2015/09/15:
Fix buffer overflow in igd_desc_parse.c/IGDstartelt()
Discovered by Aleksandar Nikolic of Cisco Talos
2015/08/28:
move ssdpDiscoverDevices() to minissdpc.c
2015/08/27:
avoid unix socket leak in getDevicesFromMiniSSDPD()
2015/08/16:
Also accept "Up" as ConnectionStatus value
2015/07/23:
split getDevicesFromMiniSSDPD
add ttl argument to upnpDiscover() functions
increments API_VERSION to 14
2015/07/22:
Read USN from SSDP messages.
2015/07/15:
Check malloc/calloc
2015/06/16:
update getDevicesFromMiniSSDPD() to process longer minissdpd
responses
2015/05/22:
add searchalltypes param to upnpDiscoverDevices()
increments API_VERSION to 13
2015/04/30:
upnpc: output version on the terminal
2015/04/27:
_BSD_SOURCE is deprecated in favor of _DEFAULT_SOURCE
fix CMakeLists.txt COMPILE_DEFINITIONS
fix getDevicesFromMiniSSDPD() not setting scope_id
improve -r command of upnpc command line tool
2014/11/17:
search all :
upnpDiscoverDevices() / upnpDiscoverAll() functions
listdevices executable
increment API_VERSION to 12
validate igd_desc_parse
2014/11/13:
increment API_VERSION to 11
2014/11/05:
simplified function GetUPNPUrls()
2014/09/11:
use remoteHost arg of DeletePortMapping
2014/09/06:
Fix python3 build
2014/07/01:
Fix parsing of IGD2 root descriptions
2014/06/10:
rename LIBSPEC to MINIUPNP_LIBSPEC
2014/05/15:
Add support for IGD2 AddAnyPortMapping and DeletePortMappingRange
2014/02/05:
handle EINPROGRESS after connect()
2014/02/03:
minixml now handle XML comments
VERSION 1.9 : released 2014/01/31
2014/01/31:
added argument remoteHost to UPNP_GetSpecificPortMappingEntry()
increment API_VERSION to 10
2013/12/09:
--help and -h arguments in upnpc.c
2013/10/07:
fixed potential buffer overrun in miniwget.c
Modified UPNP_GetValidIGD() to check for ExternalIpAddress
2013/08/01:
define MAXHOSTNAMELEN if not already done
2013/06/06:
update upnpreplyparse to allow larger values (128 chars instead of 64)
2013/05/14:
Update upnpreplyparse to take into account "empty" elements
validate upnpreplyparse.c code with "make check"
2013/05/03:
Fix Solaris build thanks to Maciej Małecki
2013/04/27:
Fix testminiwget.sh for BSD
2013/03/23:
Fixed Makefile for *BSD
2013/03/11:
Update Makefile to use JNAerator version 0.11
2013/02/11:
Fix testminiwget.sh for use with dash
Use $(DESTDIR) in Makefile
VERSION 1.8 : released 2013/02/06
2012/10/16:
fix testminiwget with no IPv6 support
2012/09/27:
Rename all include guards to not clash with C99
(7.1.3 Reserved identifiers).
2012/08/30:
Added -e option to upnpc program (set description for port mappings)
2012/08/29:
Python 3 support (thanks to Christopher Foo)
2012/08/11:
Fix a memory link in UPNP_GetValidIGD()
Try to handle scope id in link local IPv6 URL under MS Windows
2012/07/20:
Disable HAS_IP_MREQN on DragonFly BSD
2012/06/28:
GetUPNPUrls() now inserts scope into link-local IPv6 addresses
2012/06/23:
More error return checks in upnpc.c
#define MINIUPNPC_GET_SRC_ADDR enables receivedata() to get scope_id
parseURL() now parses IPv6 addresses scope
new parameter for miniwget() : IPv6 address scope
increment API_VERSION to 9
2012/06/20:
fixed CMakeLists.txt
2012/05/29
Improvements in testminiwget.sh
VERSION 1.7 : released 2012/05/24
2012/05/01:
Cleanup settings of CFLAGS in Makefile
Fix signed/unsigned integer comparaisons
2012/04/20:
Allow to specify protocol with TCP or UDP for -A option
2012/04/09:
Only try to fetch XML description once in UPNP_GetValidIGD()
Added -ansi flag to compilation, and fixed C++ comments to ANSI C comments.
2012/04/05:
minor improvements to minihttptestserver.c
2012/03/15:
upnperrors.c returns valid error string for unrecognized error codes
2012/03/08:
make minihttptestserver listen on loopback interface instead of 0.0.0.0
2012/01/25:
Maven installation thanks to Alexey Kuznetsov
2012/01/21:
Replace WIN32 macro by _WIN32
2012/01/19:
Fixes in java wrappers thanks to Alexey Kuznetsov :
https://github.com/axet/miniupnp/tree/fix-javatest/miniupnpc
Make and install .deb packages (python) thanks to Alexey Kuznetsov :
https://github.com/axet/miniupnp/tree/feature-debbuild/miniupnpc
2012/01/07:
The multicast interface can now be specified by name with IPv4.
2012/01/02:
Install man page
2011/11/25:
added header to Port Mappings list in upnpc.c
2011/10/09:
Makefile : make clean now removes jnaerator generated files.
MINIUPNPC_VERSION in miniupnpc.h (updated by make)
2011/09/12:
added rootdescURL to UPNPUrls structure.
VERSION 1.6 : released 2011/07/25
2011/07/25:
Update doc for version 1.6 release
2011/06/18:
Fix for windows in miniwget.c
2011/06/04:
display remote host in port mapping listing
2011/06/03:
Fix in make install : there were missing headers
2011/05/26:
Fix the socket leak in miniwget thanks to Richard Marsh.
Permit to add leaseduration in -a command. Display lease duration.
2011/05/15:
Try both LinkLocal and SiteLocal multicast address for SSDP in IPv6
2011/05/09:
add a test in testminiwget.sh.
more error checking in miniwget.c
2011/05/06:
Adding some tool to test and validate miniwget.c
simplified and debugged miniwget.c
2011/04/11:
moving ReceiveData() to a receivedata.c file.
parsing presentation url
adding IGD v2 WANIPv6FirewallControl commands
2011/04/10:
update of miniupnpcmodule.c
comments in miniwget.c, update in testminiwget
Adding errors codes from IGD v2
new functions in upnpc.c for IGD v2
2011/04/09:
Support for litteral ip v6 address in miniwget
2011/04/08:
Adding support for urn:schemas-upnp-org:service:WANIPv6FirewallControl:1
Updating APIVERSION
Supporting IPV6 in upnpDiscover()
Adding a -6 option to upnpc command line tool
2011/03/18:
miniwget/parseURL() : return an error when url param is null.
fixing GetListOfPortMappings()
2011/03/14:
upnpDiscover() now reporting an error code.
improvements in comments.
2011/03/11:
adding miniupnpcstrings.h.cmake and CMakeLists.txt files.
2011/02/15:
Implementation of GetListOfPortMappings()
2011/02/07:
updates to minixml to support character data starting with spaces
minixml now support CDATA
upnpreplyparse treats <NewPortListing> specificaly
change in simpleUPnPcommand to return the buffer (simplification)
2011/02/06:
Added leaseDuration argument to AddPortMapping()
Starting to implement GetListOfPortMappings()
2011/01/11:
updating wingenminiupnpcstrings.c
2011/01/04:
improving updateminiupnpcstrings.sh
VERSION 1.5 : released 2011/01/01
2010/12/21:
use NO_GETADDRINFO macro to disable the use of getaddrinfo/freeaddrinfo
2010/12/11:
Improvements on getHTTPResponse() code.
2010/12/09:
new code for miniwget that handle Chunked transfer encoding
using getHTTPResponse() in SOAP call code
Adding MANIFEST.in for 'python setup.py bdist_rpm'
2010/11/25:
changes to minissdpc.c to compile under Win32.
see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=729
2010/09/17:
Various improvement to Makefile from Michał Górny
2010/08/05:
Adding the script "external-ip.sh" from Reuben Hawkins
2010/06/09:
update to python module to match modification made on 2010/04/05
update to Java test code to match modification made on 2010/04/05
all UPNP_* function now return an error if the SOAP request failed
at HTTP level.
2010/04/17:
Using GetBestRoute() under win32 in order to find the
right interface to use.
2010/04/12:
Retrying with HTTP/1.1 if HTTP/1.0 failed. see
http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1703
2010/04/07:
avoid returning duplicates in upnpDiscover()
2010/04/05:
Create a connecthostport.h/.c with connecthostport() function
and use it in miniwget and miniupnpc.
Use getnameinfo() instead of inet_ntop or inet_ntoa
Work to make miniupnpc IPV6 compatible...
Add java test code.
Big changes in order to support device having both WANIPConnection
and WANPPPConnection.
2010/04/04:
Use getaddrinfo() instead of gethostbyname() in miniwget.
2010/01/06:
#define _DARWIN_C_SOURCE for Mac OS X
2009/12/19:
Improve MinGW32 build
2009/12/11:
adding a MSVC9 project to build the static library and executable
2009/12/10:
Fixing some compilation stuff for Windows/MinGW
2009/12/07:
adaptations in Makefile and updateminiupnpcstring.sh for AmigaOS
some fixes for Windows when using virtual ethernet adapters (it is the
case with VMWare installed).
2009/12/04:
some fixes for AmigaOS compilation
Changed HTTP version to HTTP/1.0 for Soap too (to prevent chunked
transfer encoding)
2009/12/03:
updating printIDG and testigddescparse.c for debug.
modifications to compile under AmigaOS
adding a testminiwget program
Changed miniwget to advertise itself as HTTP/1.0 to prevent chunked
transfer encoding
2009/11/26:
fixing updateminiupnpcstrings.sh to take into account
which command that does not return an error code.
VERSION 1.4 : released 2009/10/30
2009/10/16:
using Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS in python module.
2009/10/10:
Some fixes for compilation under Solaris
compilation fixes : http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1464
2009/09/21:
fixing the code to ignore EINTR during connect() calls.
2009/08/07:
Set socket timeout for connect()
Some cleanup in miniwget.c
2009/08/04:
remove multiple redirections with -d in upnpc.c
Print textual error code in upnpc.c
Ignore EINTR during the connect() and poll() calls.
2009/07/29:
fix in updateminiupnpcstrings.sh if OS name contains "/"
Sending a correct value for MX: field in SSDP request
2009/07/20:
Change the Makefile to compile under Mac OS X
Fixed a stackoverflow in getDevicesFromMiniSSDPD()
2009/07/09:
Compile under Haiku
generate miniupnpcstrings.h.in from miniupnpcstrings.h
2009/06/04:
patching to compile under CygWin and cross compile for minGW
VERSION 1.3 :
2009/04/17:
updating python module
Use strtoull() when using C99
2009/02/28:
Fixed miniwget.c for compiling under sun
2008/12/18:
cleanup in Makefile (thanks to Paul de Weerd)
minissdpc.c : win32 compatibility
miniupnpc.c : changed xmlns prefix from 'm' to 'u'
Removed NDEBUG (using DEBUG)
2008/10/14:
Added the ExternalHost argument to DeletePortMapping()
2008/10/11:
Added the ExternalHost argument to AddPortMapping()
Put a correct User-Agent: header in HTTP requests.
VERSION 1.2 :
2008/10/07:
Update docs
2008/09/25:
Integrated sameport patch from Dario Meloni : Added a "sameport"
argument to upnpDiscover().
2008/07/18:
small modif to make Clang happy :)
2008/07/17:
#define SOAPPREFIX "s" in miniupnpc.c in order to remove SOAP-ENV...
2008/07/14:
include declspec.h in installation (to /usr/include/miniupnpc)
VERSION 1.1 :
2008/07/04:
standard options for install/ln instead of gnu-specific stuff.
2008/07/03:
now builds a .dll and .lib with win32. (mingw32)
2008/04/28:
make install now install the binary of the upnpc tool
2008/04/27:
added testupnpigd.py
added error strings for miniupnpc "internal" errors
improved python module error/exception reporting.
2008/04/23:
Completely rewrite igd_desc_parse.c in order to be compatible with
Linksys WAG200G
Added testigddescparse
updated python module
VERSION 1.0 :
2008/02/21:
put some #ifdef DEBUG around DisplayNameValueList()
2008/02/18:
Improved error reporting in upnpcommands.c
UPNP_GetStatusInfo() returns LastConnectionError
2008/02/16:
better error handling in minisoap.c
improving display of "valid IGD found" in upnpc.c
2008/02/03:
Fixing UPNP_GetValidIGD()
improved make install :)
2007/12/22:
Adding upnperrors.c/h to provide a strupnperror() function
used to translate UPnP error codes to string.
2007/12/19:
Fixing getDevicesFromMiniSSDPD()
improved error reporting of UPnP functions
2007/12/18:
It is now possible to specify a different location for MiniSSDPd socket.
working with MiniSSDPd is now more efficient.
python module improved.
2007/12/16:
improving error reporting
2007/12/13:
Try to improve compatibility by using HTTP/1.0 instead of 1.1 and
XML a bit different for SOAP.
2007/11/25:
fixed select() call for linux
2007/11/15:
Added -fPIC to CFLAG for better shared library code.
2007/11/02:
Fixed a potential socket leak in miniwget2()
2007/10/16:
added a parameter to upnpDiscover() in order to allow the use of another
interface than the default multicast interface.
2007/10/12:
Fixed the creation of symbolic link in Makefile
2007/10/08:
Added man page
2007/10/02:
fixed memory bug in GetUPNPUrls()
2007/10/01:
fixes in the Makefile
Added UPNP_GetIGDFromUrl() and adapted the sample program accordingly.
Added SONAME in the shared library to please debian :)
fixed MS Windows compilation (minissdpd is not available under MS Windows).
2007/09/25:
small change to Makefile to be able to install in a different location
(default is /usr)
2007/09/24:
now compiling both shared and static library
2007/09/19:
Cosmetic changes on upnpc.c
2007/09/02:
adapting to new miniSSDPd (release version ?)
2007/08/31:
Usage of miniSSDPd to skip discovery process.
2007/08/27:
fixed python module to allow compilation with Python older than Python 2.4
2007/06/12:
Added a python module.
2007/05/19:
Fixed compilation under MinGW
2007/05/15:
fixed a memory leak in AddPortMapping()
Added testupnpreplyparse executable to check the parsing of
upnp soap messages
minixml now ignore namespace prefixes.
2007/04/26:
upnpc now displays external ip address with -s or -l
2007/04/11:
changed MINIUPNPC_URL_MAXSIZE to 128 to accommodate the "BT Voyager 210"
2007/03/19:
cleanup in miniwget.c
2007/03/01:
Small typo fix...
2007/01/30:
Now parsing the HTTP header from SOAP responses in order to
get content-length value.
2007/01/29:
Fixed the Soap Query to speedup the HTTP request.
added some Win32 DLL stuff...
2007/01/27:
Fixed some WIN32 compatibility issues
2006/12/14:
Added UPNPIGD_IsConnected() function in miniupnp.c/.h
Added UPNP_GetValidIGD() in miniupnp.c/.h
cleaned upnpc.c main(). now using UPNP_GetValidIGD()
2006/12/07:
Version 1.0-RC1 released
2006/12/03:
Minor changes to compile under SunOS/Solaris
2006/11/30:
made a minixml parser validator program
updated minixml to handle attributes correctly
2006/11/22:
Added a -r option to the upnpc sample thanks to Alexander Hubmann.
2006/11/19:
Cleanup code to make it more ANSI C compliant
2006/11/10:
detect and display local lan address.
2006/11/04:
Packets and Bytes Sent/Received are now unsigned int.
2006/11/01:
Bug fix thanks to Giuseppe D'Angelo
2006/10/31:
C++ compatibility for .h files.
Added a way to get ip Address on the LAN used to reach the IGD.
2006/10/25:
Added M-SEARCH to the services in the discovery process.
2006/10/22:
updated the Makefile to use makedepend, added a "make install"
update Makefile
2006/10/20:
fixing the description url parsing thanks to patch sent by
Wayne Dawe.
Fixed/translated some comments.
Implemented a better discover process, first looking
for IGD then for root devices (as some devices only reply to
M-SEARCH for root devices).
2006/09/02:
added freeUPNPDevlist() function.
2006/08/04:
More command line arguments checking
2006/08/01:
Added the .bat file to compile under Win32 with minGW32
2006/07/31:
Fixed the rootdesc parser (igd_desc_parse.c)
2006/07/20:
parseMSEARCHReply() is now returning the ST: line as well
starting changes to detect several UPnP devices on the network
2006/07/19:
using GetCommonLinkProperties to get down/upload bitrate
+6
View File
@@ -0,0 +1,6 @@
MiniUPnPc is a library enabling applications to access the services provided
by an UPnP "Internet Gateway Device" present on the network. In UPnP
terminology, MiniUPnPc is a UPnP Control Point.
MiniUPnPc is designed to have low footprint and the libc as unique
dependency.
File diff suppressed because it is too large Load Diff
+29
View File
@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2005-2025, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+11
View File
@@ -0,0 +1,11 @@
include README
include VERSION
include LICENSE
include DESCRIPTION
include src/miniupnpcmodule.c
include setup.py
include Makefile
include src/*.[ch]
include include/*.h
include *.h.in
include *.sh
+411
View File
@@ -0,0 +1,411 @@
# $Id: Makefile,v 1.156 2025/03/18 23:40:14 nanard Exp $
# MiniUPnP Project
# http://miniupnp.free.fr/
# https://miniupnp.tuxfamily.org/
# https://github.com/miniupnp/miniupnp
# (c) 2005-2025 Thomas Bernard
# to install use :
# $ make DESTDIR=/tmp/dummylocation install
# or
# $ INSTALLPREFIX=/usr/local make install
# or
# $ make install (default INSTALLPREFIX is /usr)
OS = $(shell $(CC) -dumpmachine)
VERSION = $(shell cat VERSION)
ifneq (, $(findstring darwin, $(OS)))
JARSUFFIX=mac
LIBTOOL ?= $(shell which libtool)
endif
ifneq (, $(findstring linux, $(OS)))
JARSUFFIX=linux
endif
ifneq (, $(findstring mingw, $(OS))$(findstring cygwin, $(OS))$(findstring msys, $(OS)))
JARSUFFIX=win32
endif
HAVE_IPV6 ?= yes
export HAVE_IPV6
# directories
INCDIR = include
SRCDIR = src
BUILD = build
CC ?= gcc
#AR = gar
#CFLAGS = -O -g
# to debug :
ASANFLAGS = -fsanitize=address -fsanitize=undefined -fsanitize=leak
#CFLAGS = -g -ggdb -O0 $(ASANFLAGS) -fno-omit-frame-pointer
#CPPFLAGS += -DDEBUG
#LDFLAGS += $(ASANFLAGS)
CFLAGS ?= -O
CFLAGS += -Wall
CFLAGS += -W -Wstrict-prototypes
CFLAGS += -fno-common
CPPFLAGS += -I$(BUILD)
CPPFLAGS += -DMINIUPNPC_SET_SOCKET_TIMEOUT
CPPFLAGS += -DMINIUPNPC_GET_SRC_ADDR
CPPFLAGS += -D_BSD_SOURCE
CPPFLAGS += -D_DEFAULT_SOURCE
ifneq (, $(findstring netbsd, $(OS)))
CPPFLAGS += -D_NETBSD_SOURCE
endif
ifeq (, $(findstring freebsd, $(OS))$(findstring darwin, $(OS)))
#CPPFLAGS += -D_POSIX_C_SOURCE=200112L
CPPFLAGS += -D_XOPEN_SOURCE=600
endif
#CFLAGS += -ansi
#CPPFLAGS += -DNO_GETADDRINFO
DEPFLAGS = -MM -MG
MKDIR = mkdir -p
INSTALL = install
SH = /bin/sh
JAVA = java
# see http://code.google.com/p/jnaerator/
#JNAERATOR = jnaerator-0.9.7.jar
#JNAERATOR = jnaerator-0.9.8-shaded.jar
#JNAERATORARGS = -library miniupnpc
#JNAERATOR = jnaerator-0.10-shaded.jar
#JNAERATOR = jnaerator-0.11-shaded.jar
# https://repo1.maven.org/maven2/com/nativelibs4java/jnaerator/0.12/jnaerator-0.12-shaded.jar
JNAERATOR = jnaerator-0.12-shaded.jar
JNAERATORARGS = -mode StandaloneJar -runtime JNAerator -library miniupnpc
#JNAERATORBASEURL = http://jnaerator.googlecode.com/files/
JNAERATORBASEURL = https://repo1.maven.org/maven2/com/nativelibs4java/jnaerator/0.12
ifneq (, $(findstring sun, $(OS))$(findstring solaris, $(OS)))
LDLIBS=-lsocket -lnsl -lresolv
CPPFLAGS += -D__EXTENSIONS__
CFLAGS += -std=c99
endif
# APIVERSION is used to build SONAME
APIVERSION = 21
SRCS = $(wildcard $(SRCDIR)/*.c)
PRIVATE_HEADERS = $(wildcard $(SRCDIR)/*.h)
LIBOBJS = $(addprefix $(BUILD)/,miniwget.o minixml.o igd_desc_parse.o minisoap.o \
miniupnpc.o upnpreplyparse.o upnpcommands.o upnperrors.o \
connecthostport.o portlistingparse.o receivedata.o upnpdev.o \
addr_is_reserved.o)
BUILDINCLUDES = $(addprefix $(BUILD)/, miniupnpcstrings.h)
OBJS = $(patsubst $(SRCDIR)/%.c,$(BUILD)/%.o,$(SRCS))
DEPS = $(patsubst $(SRCDIR)/%.c,$(BUILD)/%.d,$(SRCS))
# HEADERS to install
CPPFLAGS += -I$(INCDIR)
HEADERS = $(wildcard $(INCDIR)/*.h)
# library names
LIBRARY = $(BUILD)/libminiupnpc.a
ifneq (, $(findstring darwin, $(OS)))
SHAREDLIBRARY = $(BUILD)/libminiupnpc.dylib
SONAME = $(notdir $(basename $(SHAREDLIBRARY))).$(APIVERSION).dylib
CPPFLAGS += -D_DARWIN_C_SOURCE
else
ifeq ($(JARSUFFIX), win32)
SHAREDLIBRARY = $(BUILD)/miniupnpc.dll
else
# Linux/BSD/etc.
SHAREDLIBRARY = $(BUILD)/libminiupnpc.so
SONAME = $(notdir $(SHAREDLIBRARY)).$(APIVERSION)
endif
endif
EXECUTABLES = $(addprefix $(BUILD)/, upnpc-static upnp-listdevices-static)
EXECUTABLES_ADDTESTS = $(addprefix $(BUILD)/, testminixml minixmlvalid \
testupnpreplyparse testigddescparse testminiwget testportlistingparse)
TESTMINIXMLOBJS = $(addprefix $(BUILD)/, minixml.o igd_desc_parse.o testminixml.o)
TESTMINIWGETOBJS = $(addprefix $(BUILD)/, miniwget.o testminiwget.o connecthostport.o receivedata.o)
TESTUPNPREPLYPARSE = $(addprefix $(BUILD)/, testupnpreplyparse.o minixml.o upnpreplyparse.o)
TESTPORTLISTINGPARSE = $(addprefix $(BUILD)/, testportlistingparse.o minixml.o portlistingparse.o)
TESTADDR_IS_RESERVED = $(addprefix $(BUILD)/, testaddr_is_reserved.o addr_is_reserved.o)
TESTIGDDESCPARSE = $(addprefix $(BUILD)/, testigddescparse.o igd_desc_parse.o minixml.o \
miniupnpc.o miniwget.o upnpcommands.o upnpreplyparse.o \
minisoap.o connecthostport.o receivedata.o \
portlistingparse.o addr_is_reserved.o)
ifeq (, $(findstring amiga, $(OS)))
ifeq (, $(findstring mingw, $(OS))$(findstring cygwin, $(OS))$(findstring msys, $(OS)))
CFLAGS += -fPIC
endif
EXECUTABLES += $(BUILD)/upnpc-shared $(BUILD)/upnp-listdevices-shared
TESTMINIWGETOBJS += $(BUILD)/minissdpc.o
TESTIGDDESCPARSE += $(BUILD)/minissdpc.o
LIBOBJS += $(BUILD)/minissdpc.o
endif
LIBDIR ?= lib
# install directories
ifeq ($(strip $(PREFIX)),)
INSTALLPREFIX ?= /usr
else
INSTALLPREFIX ?= $(PREFIX)
endif
INSTALLDIRINC = $(INSTALLPREFIX)/include/miniupnpc
INSTALLDIRLIB = $(INSTALLPREFIX)/$(LIBDIR)
INSTALLDIRBIN = $(INSTALLPREFIX)/bin
INSTALLDIRMAN = $(INSTALLPREFIX)/share/man
PKGCONFIGDIR = $(INSTALLDIRLIB)/pkgconfig
FILESTOINSTALL = $(LIBRARY) $(EXECUTABLES)
ifeq (, $(findstring amiga, $(OS)))
FILESTOINSTALL += $(SHAREDLIBRARY) $(BUILD)/miniupnpc.pc
endif
.PHONY: install clean depend all check test everything \
installpythonmodule updateversion doc
all: $(LIBRARY) $(EXECUTABLES)
test: check
check: validateminixml validateminiwget validateupnpreplyparse \
validateportlistingparse validateigddescparse validateaddr_is_reserved
everything: all $(EXECUTABLES_ADDTESTS)
doc: doxygen/html/index.html
# note : the python module "wheel" is required for bdist_wheel
pythonmodule: $(LIBRARY) $(SRCDIR)/miniupnpcmodule.c setup.py
MAKE=$(MAKE) python setup.py build
MAKE=$(MAKE) python setup.py sdist
MAKE=$(MAKE) python setup.py bdist_wheel --skip-build
touch $@
installpythonmodule: pythonmodule
MAKE=$(MAKE) python setup.py install
pythonmodule3: $(LIBRARY) $(SRCDIR)/miniupnpcmodule.c setup.py
MAKE=$(MAKE) python3 setup.py build
touch $@
installpythonmodule3: pythonmodule3
MAKE=$(MAKE) python3 setup.py install
validateminixml: $(BUILD)/minixmlvalid
@echo "minixml validation test"
./$<
touch $@
validateminiwget: testminiwget.sh $(BUILD)/testminiwget $(BUILD)/minihttptestserver
@echo "miniwget validation test"
./$<
touch $@
validateupnpreplyparse: testupnpreplyparse.sh $(BUILD)/testupnpreplyparse
@echo "upnpreplyparse validation test"
./$<
touch $@
validateportlistingparse: $(BUILD)/testportlistingparse
@echo "portlistingparse validation test"
./$<
touch $@
validateigddescparse: $(BUILD)/testigddescparse
@echo "igd desc parse validation test"
./$< testdesc/new_LiveBox_desc.xml testdesc/new_LiveBox_desc.values
./$< testdesc/linksys_WAG200G_desc.xml testdesc/linksys_WAG200G_desc.values
touch $@
validateaddr_is_reserved: $(BUILD)/testaddr_is_reserved
@echo "addr_is_reserved() validation test"
./$<
touch $@
clean:
$(RM) $(LIBRARY) $(SHAREDLIBRARY) $(EXECUTABLES) $(OBJS) $(BUILDINCLUDES)
$(RM) $(EXECUTABLES_ADDTESTS)
# clean python stuff
$(RM) pythonmodule pythonmodule3
$(RM) validateminixml validateminiwget validateupnpreplyparse
$(RM) validateigddescparse
$(RM) minihttptestserver
$(RM) testaddr_is_reserved
$(RM) -r build/ dist/
#python setup.py clean
# clean jnaerator stuff
$(RM) _jnaerator.* java/miniupnpc_$(OS).jar
distclean: clean
$(RM) $(JNAERATOR) java/*.jar java/*.class out.errors.txt
updateversion: include/miniupnpc.h
cp $< $<.bak
sed 's/\(.*MINIUPNPC_API_VERSION\s\+\)[0-9]\+/\1$(APIVERSION)/' < $<.bak > $<
install: updateversion $(FILESTOINSTALL)
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRINC)
$(INSTALL) -m 644 $(HEADERS) $(DESTDIR)$(INSTALLDIRINC)
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRLIB)
$(INSTALL) -m 644 $(LIBRARY) $(DESTDIR)$(INSTALLDIRLIB)
ifeq (, $(findstring amiga, $(OS)))
$(INSTALL) -m 644 $(SHAREDLIBRARY) $(DESTDIR)$(INSTALLDIRLIB)/$(SONAME)
ln -fs $(SONAME) $(DESTDIR)$(INSTALLDIRLIB)/$(notdir $(SHAREDLIBRARY))
$(INSTALL) -d $(DESTDIR)$(PKGCONFIGDIR)
$(INSTALL) -m 644 $(BUILD)/miniupnpc.pc $(DESTDIR)$(PKGCONFIGDIR)
endif
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRBIN)
ifneq (, $(findstring amiga, $(OS)))
$(INSTALL) -m 755 $(BUILD)/upnpc-static $(DESTDIR)$(INSTALLDIRBIN)/upnpc
$(INSTALL) -m 755 $(BUILD)/upnp-listdevices-static $(DESTDIR)$(INSTALLDIRBIN)/upnp-listdevices
else
$(INSTALL) -m 755 $(BUILD)/upnpc-shared $(DESTDIR)$(INSTALLDIRBIN)/upnpc
$(INSTALL) -m 755 $(BUILD)/upnp-listdevices-shared $(DESTDIR)$(INSTALLDIRBIN)/upnp-listdevices
endif
$(INSTALL) -m 755 external-ip.sh $(DESTDIR)$(INSTALLDIRBIN)/external-ip
ifeq (, $(findstring amiga, $(OS)))
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRMAN)/man3
$(INSTALL) -m 644 man3/miniupnpc.3 $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3
ifneq (, $(findstring linux, $(OS)))
gzip -f $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3
endif
endif
install-static: updateversion $(FILESTOINSTALL)
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRINC)
$(INSTALL) -m 644 $(HEADERS) $(DESTDIR)$(INSTALLDIRINC)
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRLIB)
$(INSTALL) -m 644 $(LIBRARY) $(DESTDIR)$(INSTALLDIRLIB)
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRBIN)
$(INSTALL) -m 755 external-ip.sh $(DESTDIR)$(INSTALLDIRBIN)/external-ip
cleaninstall:
$(RM) -r $(DESTDIR)$(INSTALLDIRINC)
$(RM) $(DESTDIR)$(INSTALLDIRLIB)/$(LIBRARY)
$(RM) $(DESTDIR)$(INSTALLDIRLIB)/$(SHAREDLIBRARY)
$(BUILD)/miniupnpc.pc: VERSION
@$(MKDIR) $(@D)
$(RM) $@
echo "prefix=$(INSTALLPREFIX)" >> $@
echo "exec_prefix=\$${prefix}" >> $@
echo "libdir=\$${exec_prefix}/$(LIBDIR)" >> $@
echo "includedir=\$${prefix}/include" >> $@
echo "" >> $@
echo "Name: miniUPnPc" >> $@
echo "Description: UPnP IGD client lightweight library" >> $@
echo "URL: https://miniupnp.tuxfamily.org/" >> $@
echo "Version: $(VERSION)" >> $@
echo "Libs: -L\$${libdir} -lminiupnpc" >> $@
echo "Cflags: -I\$${includedir}" >> $@
depend: $(DEPS)
$(LIBRARY): $(LIBOBJS)
ifneq (, $(findstring darwin, $(OS)))
$(LIBTOOL) -static -o $@ $?
else
$(AR) crs $@ $?
endif
$(SHAREDLIBRARY): $(LIBOBJS)
ifneq (, $(findstring darwin, $(OS)))
# $(CC) -dynamiclib $(LDFLAGS) -Wl,-install_name,$(SONAME) -o $@ $^
$(CC) -dynamiclib $(LDFLAGS) -Wl,-install_name,$(INSTALLDIRLIB)/$(SONAME) -o $@ $^
else
$(CC) -shared $(LDFLAGS) -Wl,-soname,$(SONAME) -o $@ $^
endif
$(BUILD)/%.o: $(SRCDIR)/%.c $(BUILD)/%.d
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
$(DEPS): $(BUILDINCLUDES)
$(BUILD)/%.d: $(SRCDIR)/%.c
@$(MKDIR) $(@D)
$(CC) $(CPPFLAGS) $(DEPFLAGS) -MT $@ -o $@ $<
$(BUILD)/upnpc-static: $(BUILD)/upnpc.o $(LIBRARY)
$(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS)
$(BUILD)/upnpc-shared: $(BUILD)/upnpc.o $(SHAREDLIBRARY)
$(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS)
$(BUILD)/upnp-listdevices-static: $(BUILD)/listdevices.o $(LIBRARY)
$(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS)
$(BUILD)/upnp-listdevices-shared: $(BUILD)/listdevices.o $(SHAREDLIBRARY)
$(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS)
$(BUILD)/testminixml: $(TESTMINIXMLOBJS)
$(BUILD)/testminiwget: $(TESTMINIWGETOBJS)
$(BUILD)/minixmlvalid: $(addprefix $(BUILD)/, minixml.o minixmlvalid.o)
$(BUILD)/testupnpreplyparse: $(TESTUPNPREPLYPARSE)
$(BUILD)/testigddescparse: $(TESTIGDDESCPARSE)
$(BUILD)/testportlistingparse: $(TESTPORTLISTINGPARSE)
$(BUILD)/testaddr_is_reserved: $(TESTADDR_IS_RESERVED)
$(BUILD)/miniupnpcstrings.h: miniupnpcstrings.h.in updateminiupnpcstrings.sh VERSION
@$(MKDIR) $(@D)
$(SH) updateminiupnpcstrings.sh $@ $<
# ftp tool supplied with OpenBSD can download files from http.
jnaerator-%.jar:
wget $(JNAERATORBASEURL)/$@ || \
curl -o $@ $(JNAERATORBASEURL)/$@ || \
ftp $(JNAERATORBASEURL)/$@
jar: $(SHAREDLIBRARY) $(JNAERATOR)
$(JAVA) -jar $(JNAERATOR) $(JNAERATORARGS) \
miniupnpc.h miniupnpc_declspec.h upnpcommands.h upnpreplyparse.h \
igd_desc_parse.h miniwget.h upnperrors.h $(SHAREDLIBRARY) \
-package fr.free.miniupnp -o . -jar java/miniupnpc_$(JARSUFFIX).jar -v
mvn_install:
mvn install:install-file -Dfile=java/miniupnpc_$(JARSUFFIX).jar \
-DgroupId=com.github \
-DartifactId=miniupnp \
-Dversion=$(VERSION) \
-Dpackaging=jar \
-Dclassifier=$(JARSUFFIX) \
-DgeneratePom=true \
-DcreateChecksum=true
# make .deb packages
deb: /usr/share/pyshared/stdeb all
(python setup.py --command-packages=stdeb.command bdist_deb)
# install .deb packages
ideb:
(sudo dpkg -i deb_dist/*.deb)
/usr/share/pyshared/stdeb: /usr/share/doc/python-all-dev
(sudo apt-get install python-stdeb)
/usr/share/doc/python-all-dev:
(sudo apt-get install python-all-dev)
minihttptestserver: minihttptestserver.o
doxygen/html/index.html: Doxyfile $(SRCS) $(HEADERS) $(PRIVATE_HEADERS)
doxygen
print-%:
@echo "$* = $($*)"
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
+188
View File
@@ -0,0 +1,188 @@
# $Id: Makefile.mingw,v 1.37 2024/05/16 00:10:25 nanard Exp $
# Miniupnp project.
# http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
# (c) 2005-2024 Thomas Bernard
# This Makefile is made for MinGW
#
# To cross compile on a *nix machine :
# make -f Makefile.mingw DLLWRAP=mingw32-dllwrap CC=mingw32-gcc AR=mingw32-ar WINDRES=mingw32-windres
#
SRCDIR = src
INCDIR = include
CC ?= gcc
SETUP_COMPILER_FLAG?=
DLLWRAP = dllwrap
WINDRES = windres
SH = /bin/sh
ZIP = zip
ifeq ($(OS),Windows_NT)
RM = del
else
RM = rm -f
endif
CFLAGS ?= -Os
CFLAGS += -Wall
CFLAGS += -W -Wstrict-prototypes
CPPFLAGS += -DNDEBUG -D_WIN32_WINNT=0x501
CPPFLAGS += -Iinclude
CPPFLAGS += -I.
# -liphlpapi is needed for GetBestRoute() and GetIpAddrTable()
LDLIBS = -lws2_32 -liphlpapi
PYTHON=\utils\python25\python
OBJS=miniwget.o minixml.o igd_desc_parse.o minisoap.o \
minissdpc.o \
miniupnpc.o upnpreplyparse.o upnpcommands.o upnperrors.o \
connecthostport.o portlistingparse.o receivedata.o \
upnpdev.o addr_is_reserved.o
OBJSDLL=$(addprefix dll-, $(OBJS)) winres.o
BINARIES=upnpc-static.exe upnpc-shared.exe \
upnp-listdevices-static.exe upnp-listdevices-shared.exe \
miniupnpc.dll libminiupnpc.a \
testminixml.exe
ifneq ($(GITHUB_SHA),)
COMMITREF=$(GITHUB_SHA)
else
ifneq ($(CI_COMMIT_SHORT_SHA),)
COMMITREF=$(CI_COMMIT_SHORT_SHA)
else
COMMITREF=$(shell git rev-parse --short HEAD)
endif
endif
DISTFILE:=$(shell echo "miniupnpc-bin-win32-`cat VERSION`-$(COMMITREF).zip")
LIBDIR ?= lib
# install directories
ifeq ($(strip $(PREFIX)),)
INSTALLPREFIX ?= /usr
else
INSTALLPREFIX ?= $(PREFIX)
endif
.PHONY: all dist clean
all: $(BINARIES)
dist: $(DISTFILE)
clean:
$(RM) miniupnpcstrings.h
$(RM) *.o
$(RM) *.exe
$(RM) miniupnpc.dll miniupnpc.lib miniupnpc.dll.def
$(RM) libminiupnpc.a
$(RM) $(DISTFILE)
$(DISTFILE): $(BINARIES)
$(ZIP) $@ *.exe *.dll *.lib *.def *.a LICENSE README Changelog.txt
libminiupnpc.a: $(OBJS)
$(AR) cr $@ $?
pythonmodule: libminiupnpc.a
$(PYTHON) setupmingw32.py build $(SETUP_COMPILER_FLAG)
$(PYTHON) setupmingw32.py install --skip-build
$(PYTHON) setupmingw32.py sdist
$(PYTHON) setupmingw32.py bdist_wheel --skip-build
miniupnpc.dll: miniupnpc.def $(OBJSDLL)
$(DLLWRAP) -k --driver-name $(CC) \
--def miniupnpc.def \
--output-def miniupnpc.dll.def \
--implib miniupnpc.lib -o $@ \
$(OBJSDLL) $(LDLIBS)
miniupnpc.lib: miniupnpc.dll
%.o: $(SRCDIR)/%.c
$(CC) $(CFLAGS) $(CPPFLAGS) -DMINIUPNP_STATICLIB -c -o $@ $<
dll-%.o: $(SRCDIR)/%.c
$(CC) $(CFLAGS) $(CPPFLAGS) -DMINIUPNP_EXPORTS -c -o $@ $<
%-shared.o: $(SRCDIR)/%.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
# --enable-stdcall-fixup
%-static.exe: %.o libminiupnpc.a
$(CC) -static -o $@ $^ $(LDLIBS)
%-shared.exe: %-shared.o miniupnpc.lib
$(CC) -o $@ $^ $(LDLIBS)
upnp-listdevices-static.exe: listdevices.o libminiupnpc.a
$(CC) -static -o $@ $^ $(LDLIBS)
upnp-listdevices-shared.exe: listdevices-shared.o miniupnpc.lib
$(CC) -o $@ $^ $(LDLIBS)
# To make miniupnpcstrings.h from miniupnpcstrings.h.in we either
# use a custom executable (if running make under windows) or use
# sed (if cross compiling from another platform).
ifeq ($(OS),Windows_NT)
wingenminiupnpcstrings.exe: wingenminiupnpcstrings.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^
miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings.exe VERSION
.\wingenminiupnpcstrings.exe $< $@ rc_version.h
rc_version.h: miniupnpcstrings.h
else
miniupnpcstrings.h: miniupnpcstrings.h.in VERSION
sed 's|OS_STRING ".*"|OS_STRING "Windows/Mingw32"|' $< | \
sed 's|MINIUPNPC_VERSION_STRING ".*"|MINIUPNPC_VERSION_STRING "$(shell cat VERSION)"|' > $@
rc_version.h: VERSION
echo "#define LIBMINIUPNPC_DOTTED_VERSION \"$(shell cat VERSION)\"" > $@.tmp
echo "#define LIBMINIUPNPC_MAJOR_VERSION $(shell cat VERSION|cut -d. -f1)" >> $@.tmp
echo "#define LIBMINIUPNPC_MINOR_VERSION $(shell cat VERSION|cut -d. -f2)" >> $@.tmp
echo "#define LIBMINIUPNPC_MICRO_VERSION $(shell cat VERSION|cut -d. -f3)" >> $@.tmp
mv $@.tmp $@
endif
miniupnpc.pc: VERSION
$(RM) $@
echo "prefix=$(INSTALLPREFIX)" >> $@
echo "exec_prefix=\$${prefix}" >> $@
echo "libdir=\$${exec_prefix}/$(LIBDIR)" >> $@
echo "includedir=\$${prefix}/include" >> $@
echo "" >> $@
echo "Name: miniUPnPc" >> $@
echo "Description: UPnP IGD client lightweight library" >> $@
echo "Version: $(shell cat VERSION)" >> $@
echo "Libs: -L\$${libdir} -lminiupnpc" >> $@
echo "Cflags: -I\$${includedir}" >> $@
winres.o: miniupnpc.rc rc_version.h
$(WINDRES) -D INTERNAL_NAME=\\\"miniupnpc.dll\\0\\\" -i $< -o $@
testminixml.exe: testminixml.o minixml.o igd_desc_parse.o
$(CC) -static -o $@ $^
minixml.o: $(SRCDIR)/minixml.c $(SRCDIR)/minixml.h
upnpc.o: include/miniwget.h $(SRCDIR)/minisoap.h include/miniupnpc.h include/igd_desc_parse.h
upnpc.o: include/upnpreplyparse.h include/upnpcommands.h include/upnperrors.h miniupnpcstrings.h
miniwget.o: $(SRCDIR)/miniwget.c include/miniwget.h miniupnpcstrings.h $(SRCDIR)/connecthostport.h
minisoap.o: $(SRCDIR)/minisoap.c $(SRCDIR)/minisoap.h miniupnpcstrings.h
miniupnpc.o: $(SRCDIR)/miniupnpc.c include/miniupnpc.h $(SRCDIR)/minisoap.h \
include/miniwget.h $(SRCDIR)/minixml.h $(SRCDIR)/addr_is_reserved.h
igd_desc_parse.o: $(SRCDIR)/igd_desc_parse.c include/igd_desc_parse.h
upnpreplyparse.o: $(SRCDIR)/upnpreplyparse.c include/upnpreplyparse.h $(SRCDIR)/minixml.h
upnpcommands.o: $(SRCDIR)/upnpcommands.c include/upnpcommands.h include/upnpreplyparse.h \
include/miniupnpc.h include/portlistingparse.h
minissdpc.o: $(SRCDIR)/minissdpc.c $(SRCDIR)/minissdpc.h $(SRCDIR)/receivedata.h
upnpdev.o: $(SRCDIR)/upnpdev.c include/upnpdev.h
+92
View File
@@ -0,0 +1,92 @@
Project: miniupnp
Project web page: http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
github: https://github.com/miniupnp/miniupnp
Author: Thomas Bernard
Copyright (c) 2005-2025 Thomas Bernard
This software is subject to the conditions detailed in the
LICENSE file provided within this distribution.
* miniUPnP Client - miniUPnPc *
To compile, simply run 'gmake' (could be 'make' on your system).
Under win32, to compile with MinGW, type "mingw32make.bat".
MS Visual C solution and project files are supplied in the msvc/ subdirectory.
The miniupnpc library is available as a static library or as a DLL :
define MINIUPNP_STATICLIB if you want to link against the static library.
The compilation is known to work under linux, FreeBSD,
OpenBSD, MacOS X, AmigaOS and cygwin.
The official AmigaOS4.1 SDK was used for AmigaOS4 and GeekGadgets for AmigaOS3.
upx (http://upx.sourceforge.net) is used to compress the win32 .exe files.
To install the library and headers on the system use :
> su
> make install
> exit
alternatively, to install into a specific location, use :
> INSTALLPREFIX=/usr/local make install
upnpc.c is a sample client using the libminiupnpc.
To use the libminiupnpc in your application, link it with
libminiupnpc.a (or .so) and use the following functions found in miniupnpc.h,
upnpcommands.h and miniwget.h :
- upnpDiscover()
- UPNP_GetValidIGD()
- miniwget()
- parserootdesc()
- GetUPNPUrls()
- UPNP_* (calling UPNP methods)
Note : use #include <miniupnpc/miniupnpc.h> etc... for the includes
and -lminiupnpc for the link
Discovery process is speeded up when MiniSSDPd is running on the machine.
* Python module *
you can build a python module with 'make pythonmodule'
and install it with 'make installpythonmodule'.
setup.py (and setupmingw32.py) are included in the distribution.
Feel free to contact me if you have any problem :
e-mail : [email protected]
If you are using libminiupnpc in your application, please
send me an email !
For any question, you can use the web forum :
https://miniupnp.tuxfamily.org/forum/
Bugs should be reported on GitHub :
https://github.com/miniupnp/miniupnp/issues
* Linux firewall configuration for UPnP clients *
Due to how UPnP protocol is designed, unicast responses to UPnP multicast client
requests are not tracked by Linux netfilter. And therefore netfilter executes
default action for them (which is in most cases DROP response packet).
To workaround this limitation, custom ipset hash table can be used. It is
supported since Linux kernel >= 2.6.39.
Rules for IPv4:
$ ipset create upnp hash:ip,port timeout 3
$ iptables -A OUTPUT -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j SET --add-set upnp src,src --exist
$ iptables -A INPUT -p udp -m set --match-set upnp dst,dst -j ACCEPT
$ iptables -A INPUT -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j ACCEPT
Rules for IPv6:
$ ipset create upnp6 hash:ip,port timeout 3 family inet6
$ ip6tables -A OUTPUT -d ff02::c/128 -p udp -m udp --dport 1900 -j SET --add-set upnp6 src,src --exist
$ ip6tables -A OUTPUT -d ff05::c/128 -p udp -m udp --dport 1900 -j SET --add-set upnp6 src,src --exist
$ ip6tables -A INPUT -p udp -m set --match-set upnp6 dst,dst -j ACCEPT
$ ip6tables -A INPUT -d ff02::c/128 -p udp -m udp --dport 1900 -j ACCEPT
$ ip6tables -A INPUT -d ff05::c/128 -p udp -m udp --dport 1900 -j ACCEPT
Detailed description is available on:
https://serverfault.com/a/911286
https://unix.stackexchange.com/a/444804
+1
View File
@@ -0,0 +1 @@
2.3.3
@@ -0,0 +1,90 @@
/* $Id: addr_is_reserved.c,v 1.8 2025/05/25 21:56:48 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas BERNARD
* copyright (c) 2005-2025 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENSE file. */
#ifdef _WIN32
/* Win32 Specific includes and defines */
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#if !defined(_MSC_VER)
#include <stdint.h>
#else /* !defined(_MSC_VER) */
typedef unsigned long uint32_t;
#endif /* !defined(_MSC_VER) */
#if !defined(_WIN32_WINNT_VISTA)
#define _WIN32_WINNT_VISTA 0x0600
#endif
#else /* _WIN32 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif /* _WIN32 */
#ifdef DEBUG
#include <stdio.h>
#endif
/* List of IP address blocks which are private / reserved and therefore not suitable for public external IP addresses */
#define IP(a, b, c, d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
#define MSK(m) (32-(m))
static const struct { uint32_t address; uint32_t rmask; } reserved[] = {
{ IP( 0, 0, 0, 0), MSK( 8) }, /* RFC1122 "This host on this network" */
{ IP( 10, 0, 0, 0), MSK( 8) }, /* RFC1918 Private-Use */
{ IP(100, 64, 0, 0), MSK(10) }, /* RFC6598 Shared Address Space */
{ IP(127, 0, 0, 0), MSK( 8) }, /* RFC1122 Loopback */
{ IP(169, 254, 0, 0), MSK(16) }, /* RFC3927 Link-Local */
{ IP(172, 16, 0, 0), MSK(12) }, /* RFC1918 Private-Use */
{ IP(192, 0, 0, 0), MSK(24) }, /* RFC6890 IETF Protocol Assignments */
{ IP(192, 0, 2, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-1) */
{ IP(192, 31, 196, 0), MSK(24) }, /* RFC7535 AS112-v4 */
{ IP(192, 52, 193, 0), MSK(24) }, /* RFC7450 AMT */
{ IP(192, 88, 99, 0), MSK(24) }, /* RFC7526 6to4 Relay Anycast */
{ IP(192, 168, 0, 0), MSK(16) }, /* RFC1918 Private-Use */
{ IP(192, 175, 48, 0), MSK(24) }, /* RFC7534 Direct Delegation AS112 Service */
{ IP(198, 18, 0, 0), MSK(15) }, /* RFC2544 Benchmarking */
{ IP(198, 51, 100, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-2) */
{ IP(203, 0, 113, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-3) */
{ IP(224, 0, 0, 0), MSK( 4) }, /* RFC1112 Multicast */
{ IP(240, 0, 0, 0), MSK( 4) }, /* RFC1112 Reserved for Future Use + RFC919 Limited Broadcast */
};
#undef IP
#undef MSK
/**
* @return 1 or 0
*/
int addr_is_reserved(const char * addr_str)
{
uint32_t addr_n, address;
size_t i;
#if defined(_WIN32) && (_WIN32_WINNT < _WIN32_WINNT_VISTA)
addr_n = inet_addr(addr_str);
if (addr_n == INADDR_NONE)
return 1;
#else
/* was : addr_n = inet_addr(addr_str); */
if (inet_pton(AF_INET, addr_str, &addr_n) <= 0) {
/* error */
return 1;
}
#endif
address = ntohl(addr_n);
for (i = 0; i < sizeof(reserved)/sizeof(reserved[0]); ++i) {
if ((address >> reserved[i].rmask) == (reserved[i].address >> reserved[i].rmask)) {
#ifdef DEBUG
printf("IP address %s is reserved\n", addr_str);
#endif
return 1;
}
}
return 0;
}
@@ -0,0 +1,14 @@
/* $Id: addr_is_reserved.h,v 1.1 2020/09/28 21:11:19 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project: miniupnp
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author: Thomas Bernard
* Copyright (c) 2005-2020 Thomas Bernard
* This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */
#ifndef ADDR_IS_RESERVED_H_INCLUDED
#define ADDR_IS_RESERVED_H_INCLUDED
int addr_is_reserved(const char * addr_str);
#endif /* ADDR_IS_RESERVED_H_INCLUDED */
+204
View File
@@ -0,0 +1,204 @@
$Id: apiversions.txt,v 1.15 2025/03/18 23:40:14 nanard Exp $
Differences in API between miniUPnPc versions
API version 21
Add UPNP_GetSpecificPortMappingEntryExt() and
UPNP_GetGenericPortMappingEntryExt() functions to support retrieving
arbitrary long descriptions.
updated macro :
#define MINIUPNPC_API_VERSION 21
API version 20
cleanup : remove STRTOUI and parseURL() from public headers
remove struct NameValue from upnpreplyparse.h
updated macro :
#define MINIUPNPC_API_VERSION 20
This more a cleanup than a real API change. Things that were removed
from public headers were not supposed to be used and/or not
exposed in the dll export list.
API version 19
change simpleUPnPcommand() prototype
updated macro :
#define MINIUPNPC_API_VERSION 19
API version 18
change UPNP_GetValidIGD() prototype and return values
updated macro :
#define MINIUPNPC_API_VERSION 18
API version 17
change struct UPNPDev
move getHTTPResponse() to miniwget_private.h
updated macro :
#define MINIUPNPC_API_VERSION 17
API version 16
added "status_code" argument to getHTTPResponse(), miniwget() and miniwget_getaddr()
updated macro :
#define MINIUPNPC_API_VERSION 16
API version 15
changed "sameport" argument of upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
to "localport". When 0 or 1, behaviour is not changed, but it can take
any other value between 2 and 65535
Existing programs should be compatible
updated macro :
#define MINIUPNPC_API_VERSION 15
API version 14
miniupnpc.h
add ttl argument to upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
upnpDiscoverDevices()
getDevicesFromMiniSSDPD() :
connectToMiniSSDPD() / disconnectFromMiniSSDPD()
requestDevicesFromMiniSSDPD() / receiveDevicesFromMiniSSDPD()
updated macro :
#define MINIUPNPC_API_VERSION 14
API version 13
miniupnpc.h:
add searchalltype param to upnpDiscoverDevices() function
updated macro :
#define MINIUPNPC_API_VERSION 13
API version 12
miniupnpc.h :
add upnpDiscoverAll() / upnpDiscoverDevice() / upnpDiscoverDevices()
functions
updated macros :
#define MINIUPNPC_API_VERSION 12
API version 11
upnpreplyparse.h / portlistingparse.h :
removed usage of sys/queue.h / bsdqueue.h
miniupnpc.h:
updated macros :
#define MINIUPNPC_API_VERSION 11
====================== miniUPnPc version 1.9 ======================
API version 10
upnpcommands.h:
added argument remoteHost to UPNP_GetSpecificPortMappingEntry()
miniupnpc.h:
updated macros :
#define MINIUPNPC_VERSION "1.9"
#define MINIUPNPC_API_VERSION 10
====================== miniUPnPc version 1.8 ======================
API version 9
miniupnpc.h:
updated macros :
#define MINIUPNPC_VERSION "1.8"
#define MINIUPNPC_API_VERSION 9
added "unsigned int scope_id;" to struct UPNPDev
added scope_id argument to GetUPNPUrls()
====================== miniUPnPc version 1.7 ======================
API version 8
miniupnpc.h :
add new macros :
#define MINIUPNPC_VERSION "1.7"
#define MINIUPNPC_API_VERSION 8
add rootdescURL to struct UPNPUrls
====================== miniUPnPc version 1.6 ======================
API version 8
Adding support for IPv6.
igd_desc_parse.h :
struct IGDdatas_service :
add char presentationurl[MINIUPNPC_URL_MAXSIZE];
struct IGDdatas :
add struct IGDdatas_service IPv6FC;
miniupnpc.h :
new macros :
#define UPNPDISCOVER_SUCCESS (0)
#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
#define UPNPDISCOVER_SOCKET_ERROR (-101)
#define UPNPDISCOVER_MEMORY_ERROR (-102)
simpleUPnPcommand() prototype changed (but is normaly not used by API users)
add arguments ipv6 and error to upnpDiscover() :
struct UPNPDev *
upnpDiscover(int delay, const char * multicastif,
const char * minissdpdsock, int sameport,
int ipv6,
int * error);
add controlURL_6FC member to struct UPNPUrls :
struct UPNPUrls {
char * controlURL;
char * ipcondescURL;
char * controlURL_CIF;
char * controlURL_6FC;
};
upnpcommands.h :
add leaseDuration argument to UPNP_AddPortMapping()
add desc, enabled and leaseDuration arguments to UPNP_GetSpecificPortMappingEntry()
add UPNP_GetListOfPortMappings() function (IGDv2)
add IGDv2 IPv6 related functions :
UPNP_GetFirewallStatus()
UPNP_GetOutboundPinholeTimeout()
UPNP_AddPinhole()
UPNP_UpdatePinhole()
UPNP_DeletePinhole()
UPNP_CheckPinholeWorking()
UPNP_GetPinholePackets()
====================== miniUPnPc version 1.5 ======================
API version 5
new function :
int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
new macro in upnpcommands.h :
#define UPNPCOMMAND_HTTP_ERROR
====================== miniUPnPc version 1.4 ======================
Same API as version 1.3
====================== miniUPnPc version 1.3 ======================
API version 4
Use UNSIGNED_INTEGER type for
UPNP_GetTotalBytesSent(), UPNP_GetTotalBytesReceived(),
UPNP_GetTotalPacketsSent(), UPNP_GetTotalPacketsReceived()
Add remoteHost argument to UPNP_AddPortMapping() and UPNP_DeletePortMapping()
====================== miniUPnPc version 1.2 ======================
API version 3
added sameport argument to upnpDiscover()
struct UPNPDev *
upnpDiscover(int delay, const char * multicastif,
const char * minissdpdsock, int sameport);
====================== miniUPnPc Version 1.1 ======================
Same API as 1.0
====================== miniUPnPc Version 1.0 ======================
API version 2
struct UPNPDev {
struct UPNPDev * pNext;
char * descURL;
char * st;
char buffer[2];
};
struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
const char * minissdpdsock);
+54
View File
@@ -0,0 +1,54 @@
/* $Id: codelength.h,v 1.5 2015/07/09 12:40:18 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas BERNARD
* copyright (c) 2005-2015 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
#ifndef CODELENGTH_H_INCLUDED
#define CODELENGTH_H_INCLUDED
/* Encode length by using 7bit per Byte :
* Most significant bit of each byte specifies that the
* following byte is part of the code */
/* n : unsigned
* p : unsigned char *
*/
#define DECODELENGTH(n, p) n = 0; \
do { n = (n << 7) | (*p & 0x7f); } \
while((*(p++)&0x80) && (n<(1<<25)));
/* n : unsigned
* READ : function/macro to read one byte (unsigned char)
*/
#define DECODELENGTH_READ(n, READ) \
n = 0; \
do { \
unsigned char c; \
READ(c); \
n = (n << 7) | (c & 0x07f); \
if(!(c&0x80)) break; \
} while(n<(1<<25));
/* n : unsigned
* p : unsigned char *
* p_limit : unsigned char *
*/
#define DECODELENGTH_CHECKLIMIT(n, p, p_limit) \
n = 0; \
do { \
if((p) >= (p_limit)) break; \
n = (n << 7) | (*(p) & 0x7f); \
} while((*((p)++)&0x80) && (n<(1<<25)));
/* n : unsigned
* p : unsigned char *
*/
#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \
if(n>=2097152) *(p++) = (n >> 21) | 0x80; \
if(n>=16384) *(p++) = (n >> 14) | 0x80; \
if(n>=128) *(p++) = (n >> 7) | 0x80; \
*(p++) = n & 0x7f;
#endif /* CODELENGTH_H_INCLUDED */
@@ -0,0 +1,290 @@
/* $Id: connecthostport.c,v 1.26 2025/05/25 21:56:48 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
* Copyright (c) 2010-2025 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
/* use getaddrinfo() or gethostbyname()
* uncomment the following line in order to use gethostbyname() */
#ifdef NO_GETADDRINFO
#define USE_GETHOSTBYNAME
#endif
#include <string.h>
#include <stdio.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#include <io.h>
#define MAXHOSTNAMELEN 64
#include "win32_snprintf.h"
#define herror
#define socklen_t int
#else /* #ifdef _WIN32 */
#include <unistd.h>
#include <sys/types.h>
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
#include <sys/time.h>
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
#include <sys/param.h>
#include <sys/select.h>
#include <errno.h>
#define closesocket close
#include <netdb.h>
#include <netinet/in.h>
/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
* during the connect() call */
#define MINIUPNPC_IGNORE_EINTR
#include <sys/socket.h>
#include <sys/select.h>
#endif /* #else _WIN32 */
#if defined(__amigaos__) || defined(__amigaos4__)
#define herror(A) printf("%s\n", A)
#endif
#include "connecthostport.h"
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif
/* connecthostport()
* return a socket connected (TCP) to the host and port
* or -1 in case of error */
SOCKET connecthostport(const char * host, unsigned short port,
unsigned int scope_id)
{
SOCKET s;
int n;
#ifdef USE_GETHOSTBYNAME
struct sockaddr_in dest;
struct hostent *hp;
#else /* #ifdef USE_GETHOSTBYNAME */
char tmp_host[MAXHOSTNAMELEN+1];
char port_str[8];
struct addrinfo *ai, *p;
struct addrinfo hints;
#endif /* #ifdef USE_GETHOSTBYNAME */
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
struct timeval timeout;
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
#ifdef USE_GETHOSTBYNAME
hp = gethostbyname(host);
if(hp == NULL)
{
herror(host);
return INVALID_SOCKET;
}
memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));
memset(dest.sin_zero, 0, sizeof(dest.sin_zero));
s = socket(PF_INET, SOCK_STREAM, 0);
if(ISINVALID(s))
{
PRINT_SOCKET_ERROR("socket");
return INVALID_SOCKET;
}
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
/* setting a 3 seconds timeout for the connect() call */
timeout.tv_sec = 3;
timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
{
PRINT_SOCKET_ERROR("setsockopt SO_RCVTIMEO");
}
timeout.tv_sec = 3;
timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
{
PRINT_SOCKET_ERROR("setsockopt SO_SNDTIMEO");
}
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
dest.sin_family = AF_INET;
dest.sin_port = htons(port);
n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));
#ifdef MINIUPNPC_IGNORE_EINTR
/* EINTR The system call was interrupted by a signal that was caught
* EINPROGRESS The socket is nonblocking and the connection cannot
* be completed immediately. */
while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
{
socklen_t len;
fd_set wset;
int err;
FD_ZERO(&wset);
FD_SET(s, &wset);
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
timeout.tv_sec = 3;
timeout.tv_usec = 0;
n = select(s + 1, NULL, &wset, NULL, &timeout);
#else
n = select(s + 1, NULL, &wset, NULL, NULL);
#endif
if(n < 0) {
if (errno == EINTR)
continue; /* try again */
else
break; /* EBADF, EFAULT, EINVAL */
}
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
if(n == 0) {
errno = ETIMEDOUT;
n = -1;
break;
}
#endif
len = sizeof(err);
if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
PRINT_SOCKET_ERROR("getsockopt");
closesocket(s);
return INVALID_SOCKET;
}
if(err != 0) {
errno = err;
n = -1;
}
}
#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
if(n<0)
{
PRINT_SOCKET_ERROR("connect");
closesocket(s);
return INVALID_SOCKET;
}
#else /* #ifdef USE_GETHOSTBYNAME */
/* use getaddrinfo() instead of gethostbyname() */
memset(&hints, 0, sizeof(hints));
/* hints.ai_flags = AI_ADDRCONFIG; */
#ifdef AI_NUMERICSERV
hints.ai_flags = AI_NUMERICSERV;
#endif
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
/* hints.ai_protocol = IPPROTO_TCP; */
snprintf(port_str, sizeof(port_str), "%hu", port);
if(host[0] == '[')
{
/* literal ip v6 address */
int i, j;
for(i = 0, j = 1; host[j] && (host[j] != ']') && i < MAXHOSTNAMELEN; i++, j++)
{
tmp_host[i] = host[j];
if(0 == strncmp(host+j, "%25", 3)) /* %25 is just url encoding for '%' */
j+=2; /* skip "25" */
}
tmp_host[i] = '\0';
}
else
{
strncpy(tmp_host, host, MAXHOSTNAMELEN);
}
tmp_host[MAXHOSTNAMELEN] = '\0';
n = getaddrinfo(tmp_host, port_str, &hints, &ai);
if(n != 0)
{
#ifdef _WIN32
fprintf(stderr, "getaddrinfo() error : %d\n", n);
#else
fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
#endif
return INVALID_SOCKET;
}
s = INVALID_SOCKET;
for(p = ai; p; p = p->ai_next)
{
if(!ISINVALID(s))
closesocket(s);
#ifdef DEBUG
printf("ai_family=%d ai_socktype=%d ai_protocol=%d (PF_INET=%d, PF_INET6=%d)\n",
p->ai_family, p->ai_socktype, p->ai_protocol, PF_INET, PF_INET6);
#endif
s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if(ISINVALID(s))
continue;
if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) {
struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *)p->ai_addr;
addr6->sin6_scope_id = scope_id;
}
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
/* setting a 3 seconds timeout for the connect() call */
timeout.tv_sec = 3;
timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
{
PRINT_SOCKET_ERROR("setsockopt");
}
timeout.tv_sec = 3;
timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
{
PRINT_SOCKET_ERROR("setsockopt");
}
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
n = connect(s, p->ai_addr, MSC_CAST_INT p->ai_addrlen);
#ifdef MINIUPNPC_IGNORE_EINTR
/* EINTR The system call was interrupted by a signal that was caught
* EINPROGRESS The socket is nonblocking and the connection cannot
* be completed immediately. */
while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
{
socklen_t len;
fd_set wset;
int err;
FD_ZERO(&wset);
FD_SET(s, &wset);
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
timeout.tv_sec = 3;
timeout.tv_usec = 0;
n = select(s + 1, NULL, &wset, NULL, &timeout);
#else
n = select(s + 1, NULL, &wset, NULL, NULL);
#endif
if(n < 0) {
if (errno == EINTR)
continue; /* try again */
else
break; /* EBADF, EFAULT, EINVAL */
}
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
if(n == 0) {
errno = ETIMEDOUT;
n = -1;
break;
}
#endif
len = sizeof(err);
if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
PRINT_SOCKET_ERROR("getsockopt");
closesocket(s);
freeaddrinfo(ai);
return INVALID_SOCKET;
}
if(err != 0) {
errno = err;
n = -1;
}
}
#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
if(n >= 0) /* connect() was successful */
break;
}
freeaddrinfo(ai);
if(ISINVALID(s))
{
PRINT_SOCKET_ERROR("socket");
return INVALID_SOCKET;
}
if(n < 0)
{
PRINT_SOCKET_ERROR("connect");
closesocket(s);
return INVALID_SOCKET;
}
#endif /* #ifdef USE_GETHOSTBYNAME */
return s;
}
@@ -0,0 +1,20 @@
/* $Id: connecthostport.h,v 1.4 2018/04/06 10:53:13 nanard Exp $ */
/* Project: miniupnp
* http://miniupnp.free.fr/
* Author: Thomas Bernard
* Copyright (c) 2010-2018 Thomas Bernard
* This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */
#ifndef CONNECTHOSTPORT_H_INCLUDED
#define CONNECTHOSTPORT_H_INCLUDED
#include "miniupnpc_socketdef.h"
/* connecthostport()
* return a socket connected (TCP) to the host and port
* or INVALID_SOCKET in case of error */
SOCKET connecthostport(const char * host, unsigned short port,
unsigned int scope_id);
#endif
@@ -0,0 +1,4 @@
#!/bin/sh
# $Id: external-ip.sh,v 1.2 2017/11/02 15:33:09 nanard Exp $
# (c) 2010 Reuben Hawkins
upnpc -s | sed -n -e 's/^ExternalIPAddress = \([0-9.]*\)$/\1/p'
@@ -0,0 +1,112 @@
' VBScript to generate miniupnpcstrings.h
' Copyright 2018 Thomas Bernard
'Set WshShell = CreateObject("WScript.Shell")
Set FSO = CreateObject("Scripting.FileSystemObject")
versionfile = "..\version"
infile = "..\miniupnpcstrings.h.in"
outfile = "..\miniupnpcstrings.h"
outfilerc = "..\rc_version.h"
On Error Resume Next
'Wscript.Echo revision
Err.Clear
Set f = FSO.OpenTextFile(versionfile, 1, False) ' 1 = Read
If Err.Number = 0 Then
version = f.ReadLine
f.Close
Else
' Exit error
WScript.Quit 1
End If
os_version = "0.0.0"
strComputer = "."
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colOperatingSystems = objWMIService.ExecQuery ("Select * from Win32_OperatingSystem")
For Each objOperatingSystem in colOperatingSystems
'Wscript.Echo objOperatingSystem.Caption & " -- "
os_version = objOperatingSystem.Version
Next
'Wscript.Echo os_version
Dim array
needWrite = True
' First Check if the file already contains the right versions
Err.Clear
Set f_in = FSO.OpenTextFile(outfile, 1, False)
If Err.Number = 0 Then
old_version = ""
old_os_version = ""
Do Until f_in.AtEndOfStream
line = f_in.ReadLine
If Len(line) > 0 Then
array = Split(line, " ")
If UBound(array) >= 2 And array(0) = "#define" Then
If array(1) = "OS_STRING" Then
old_os_version = Replace(array(2), Chr(34), "")
ElseIf array(1) = "MINIUPNPC_VERSION_STRING" Then
old_version = Replace(array(2), Chr(34), "")
End if
End if
End If
Loop
f_in.Close
If old_version = version And old_os_version = "MSWindows/" & os_version Then
needWrite = False
Else
needWrite = True
End If
End If
If Not needWrite Then
' check files dates
Set fIn1 = FSO.GetFile(versionfile)
Set fIn2 = FSO.GetFile(infile)
Set fOut = FSO.GetFile(outfile)
If DateDiff("s", fIn1.DateLastModified, fOut.DateLastModified) < 0 Then
needWrite = True
End If
If DateDiff("s", fIn2.DateLastModified, fOut.DateLastModified) < 0 Then
needWrite = True
End If
End If
If Not needWrite Then
' nothing to do
WScript.Quit 0
End if
' generate the file
Err.Clear
Set f_in = FSO.OpenTextFile(infile, 1, False)
If Err.Number = 0 Then
Set f_out = FSO.OpenTextFile(outfile, 2, True) ' 2 = Write
Do Until f_in.AtEndOfStream
line = f_in.ReadLine
If Len(line) > 0 Then
array = Split(line, " ")
If UBound(array) >= 2 And array(0) = "#define" Then
If array(1) = "OS_STRING" Then
line = "#define OS_STRING " & Chr(34) & "MSWindows/" & os_version & Chr(34)
ElseIf array(1) = "MINIUPNPC_VERSION_STRING" Then
line = "#define MINIUPNPC_VERSION_STRING " & Chr(34) & version & Chr(34)
End if
End if
End If
f_out.WriteLine line
Loop
f_in.Close
f_out.Close
End If
Set f_out = FSO.OpenTextFile(outfilerc, 2, True) ' 2 = Write
f_out.WriteLine "#define LIBMINIUPNPC_DOTTED_VERSION " & Chr(34) & version & Chr(34)
ver = Split(version, ".")
f_out.WriteLine "#define LIBMINIUPNPC_MAJOR_VERSION " & ver(0)
f_out.WriteLine "#define LIBMINIUPNPC_MINOR_VERSION " & ver(1)
f_out.WriteLine "#define LIBMINIUPNPC_MICRO_VERSION " & ver(2)
f_out.Close
+123
View File
@@ -0,0 +1,123 @@
/* $Id: igd_desc_parse.c,v 1.17 2015/09/15 13:30:04 nanard Exp $ */
/* Project : miniupnp
* http://miniupnp.free.fr/
* Author : Thomas Bernard
* Copyright (c) 2005-2015 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
#include "igd_desc_parse.h"
#include <stdio.h>
#include <string.h>
/* Start element handler :
* update nesting level counter and copy element name */
void IGDstartelt(void * d, const char * name, int l)
{
struct IGDdatas * datas = (struct IGDdatas *)d;
if(l >= MINIUPNPC_URL_MAXSIZE)
l = MINIUPNPC_URL_MAXSIZE-1;
memcpy(datas->cureltname, name, l);
datas->cureltname[l] = '\0';
datas->level++;
if( (l==7) && !memcmp(name, "service", l) ) {
datas->tmp.controlurl[0] = '\0';
datas->tmp.eventsuburl[0] = '\0';
datas->tmp.scpdurl[0] = '\0';
datas->tmp.servicetype[0] = '\0';
}
}
#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
/* End element handler :
* update nesting level counter and update parser state if
* service element is parsed */
void IGDendelt(void * d, const char * name, int l)
{
struct IGDdatas * datas = (struct IGDdatas *)d;
datas->level--;
/*printf("endelt %2d %.*s\n", datas->level, l, name);*/
if( (l==7) && !memcmp(name, "service", l) )
{
if(COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) {
memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
} else if(COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANIPv6FirewallControl:")) {
memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service));
} else if(COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANIPConnection:")
|| COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANPPPConnection:") ) {
if(datas->first.servicetype[0] == '\0') {
memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
} else {
memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service));
}
}
}
}
/* Data handler :
* copy data depending on the current element name and state */
void IGDdata(void * d, const char * data, int l)
{
struct IGDdatas * datas = (struct IGDdatas *)d;
char * dstmember = 0;
/*printf("%2d %s : %.*s\n",
datas->level, datas->cureltname, l, data); */
if( !strcmp(datas->cureltname, "URLBase") )
dstmember = datas->urlbase;
else if( !strcmp(datas->cureltname, "presentationURL") )
dstmember = datas->presentationurl;
else if( !strcmp(datas->cureltname, "serviceType") )
dstmember = datas->tmp.servicetype;
else if( !strcmp(datas->cureltname, "controlURL") )
dstmember = datas->tmp.controlurl;
else if( !strcmp(datas->cureltname, "eventSubURL") )
dstmember = datas->tmp.eventsuburl;
else if( !strcmp(datas->cureltname, "SCPDURL") )
dstmember = datas->tmp.scpdurl;
/* else if( !strcmp(datas->cureltname, "deviceType") )
dstmember = datas->devicetype_tmp;*/
if(dstmember)
{
if(l>=MINIUPNPC_URL_MAXSIZE)
l = MINIUPNPC_URL_MAXSIZE-1;
memcpy(dstmember, data, l);
dstmember[l] = '\0';
}
}
#ifdef DEBUG
void printIGD(struct IGDdatas * d)
{
printf("urlbase = '%s'\n", d->urlbase);
printf("WAN Device (Common interface config) :\n");
/*printf(" deviceType = '%s'\n", d->CIF.devicetype);*/
printf(" serviceType = '%s'\n", d->CIF.servicetype);
printf(" controlURL = '%s'\n", d->CIF.controlurl);
printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl);
printf(" SCPDURL = '%s'\n", d->CIF.scpdurl);
printf("primary WAN Connection Device (IP or PPP Connection):\n");
/*printf(" deviceType = '%s'\n", d->first.devicetype);*/
printf(" servicetype = '%s'\n", d->first.servicetype);
printf(" controlURL = '%s'\n", d->first.controlurl);
printf(" eventSubURL = '%s'\n", d->first.eventsuburl);
printf(" SCPDURL = '%s'\n", d->first.scpdurl);
printf("secondary WAN Connection Device (IP or PPP Connection):\n");
/*printf(" deviceType = '%s'\n", d->second.devicetype);*/
printf(" servicetype = '%s'\n", d->second.servicetype);
printf(" controlURL = '%s'\n", d->second.controlurl);
printf(" eventSubURL = '%s'\n", d->second.eventsuburl);
printf(" SCPDURL = '%s'\n", d->second.scpdurl);
printf("WAN IPv6 Firewall Control :\n");
/*printf(" deviceType = '%s'\n", d->IPv6FC.devicetype);*/
printf(" servicetype = '%s'\n", d->IPv6FC.servicetype);
printf(" controlURL = '%s'\n", d->IPv6FC.controlurl);
printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl);
printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl);
}
#endif /* DEBUG */
@@ -0,0 +1,75 @@
/* $Id: igd_desc_parse.h,v 1.14 2025/02/08 23:15:16 nanard Exp $ */
/* Project : miniupnp
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
* Copyright (c) 2005-2025 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
#ifndef IGD_DESC_PARSE_H_INCLUDED
#define IGD_DESC_PARSE_H_INCLUDED
/*! \file igd_desc_parse.h
* \brief API to parse UPNP device description XML
* \todo should not be exposed in the public API
*/
/*! \brief maximum lenght of URLs */
#define MINIUPNPC_URL_MAXSIZE (128)
/*! \brief Structure to store the result of the parsing of UPnP
* descriptions of Internet Gateway Devices services */
struct IGDdatas_service {
/*! \brief controlURL for the service */
char controlurl[MINIUPNPC_URL_MAXSIZE];
/*! \brief eventSubURL for the service */
char eventsuburl[MINIUPNPC_URL_MAXSIZE];
/*! \brief SCPDURL for the service */
char scpdurl[MINIUPNPC_URL_MAXSIZE];
/*! \brief serviceType */
char servicetype[MINIUPNPC_URL_MAXSIZE];
/*char devicetype[MINIUPNPC_URL_MAXSIZE];*/
};
/*! \brief Structure to store the result of the parsing of UPnP
* descriptions of Internet Gateway Devices */
struct IGDdatas {
/*! \brief current element name */
char cureltname[MINIUPNPC_URL_MAXSIZE];
/*! \brief URLBase */
char urlbase[MINIUPNPC_URL_MAXSIZE];
/*! \brief presentationURL */
char presentationurl[MINIUPNPC_URL_MAXSIZE];
/*! \brief depth into the XML tree */
int level;
/*! \brief "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
struct IGDdatas_service CIF;
/*! \brief first of "urn:schemas-upnp-org:service:WANIPConnection:1"
* or "urn:schemas-upnp-org:service:WANPPPConnection:1" */
struct IGDdatas_service first;
/*! \brief second of "urn:schemas-upnp-org:service:WANIPConnection:1"
* or "urn:schemas-upnp-org:service:WANPPPConnection:1" */
struct IGDdatas_service second;
/*! \brief "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */
struct IGDdatas_service IPv6FC;
/*! \brief currently parsed service */
struct IGDdatas_service tmp;
};
/*!
* \brief XML start element handler
*/
void IGDstartelt(void *, const char *, int);
/*!
* \brief XML end element handler
*/
void IGDendelt(void *, const char *, int);
/*!
* \brief XML characted data handler
*/
void IGDdata(void *, const char *, int);
#ifdef DEBUG
void printIGD(struct IGDdatas *);
#endif /* DEBUG */
#endif /* IGD_DESC_PARSE_H_INCLUDED */
@@ -0,0 +1,98 @@
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import fr.free.miniupnp.*;
/**
*
* @author syuu
*/
public class JavaBridgeTest {
public static void main(String[] args) {
int UPNP_DELAY = 2000;
MiniupnpcLibrary miniupnpc = MiniupnpcLibrary.INSTANCE;
UPNPDev devlist = null;
UPNPUrls urls = new UPNPUrls();
IGDdatas data = new IGDdatas();
ByteBuffer lanaddr = ByteBuffer.allocate(16);
ByteBuffer wanaddr = ByteBuffer.allocate(16);
ByteBuffer intClient = ByteBuffer.allocate(16);
ByteBuffer intPort = ByteBuffer.allocate(6);
ByteBuffer desc = ByteBuffer.allocate(80);
ByteBuffer enabled = ByteBuffer.allocate(4);
ByteBuffer leaseDuration = ByteBuffer.allocate(16);
int ret;
int i;
if(args.length < 2) {
System.err.println("Usage : java [...] JavaBridgeTest port protocol");
System.out.println(" port is numeric, protocol is TCP or UDP");
return;
}
devlist = miniupnpc.upnpDiscover(UPNP_DELAY, (String) null, (String) null, 0, 0, (byte)2, IntBuffer.allocate(1));
if (devlist != null) {
System.out.println("List of UPNP devices found on the network :");
for (UPNPDev device = devlist; device != null; device = device.pNext) {
System.out.println("desc: " + device.descURL.getString(0) + " st: " + device.st.getString(0));
}
if ((i = miniupnpc.UPNP_GetValidIGD(devlist, urls, data, lanaddr, 16, wanaddr, 16)) != 0) {
switch (i) {
case 1:
System.out.println("Found valid IGD : " + urls.controlURL.getString(0));
break;
case 2:
System.out.println("Found a (not connected?) IGD : " + urls.controlURL.getString(0));
System.out.println("Trying to continue anyway");
break;
case 3:
System.out.println("UPnP device found. Is it an IGD ? : " + urls.controlURL.getString(0));
System.out.println("Trying to continue anyway");
break;
default:
System.out.println("Found device (igd ?) : " + urls.controlURL.getString(0));
System.out.println("Trying to continue anyway");
}
System.out.println("Local LAN ip address : " + new String(lanaddr.array()));
ByteBuffer externalAddress = ByteBuffer.allocate(16);
miniupnpc.UPNP_GetExternalIPAddress(urls.controlURL.getString(0),
new String(data.first.servicetype), externalAddress);
System.out.println("ExternalIPAddress = " + new String(externalAddress.array()));
ret = miniupnpc.UPNP_AddPortMapping(
urls.controlURL.getString(0), // controlURL
new String(data.first.servicetype), // servicetype
args[0], // external Port
args[0], // internal Port
new String(lanaddr.array()), // internal client
"added via miniupnpc/JAVA !", // description
args[1], // protocol UDP or TCP
null, // remote host (useless)
"0"); // leaseDuration
if (ret != MiniupnpcLibrary.UPNPCOMMAND_SUCCESS)
System.out.println("AddPortMapping() failed with code " + ret);
ret = miniupnpc.UPNP_GetSpecificPortMappingEntry(
urls.controlURL.getString(0), new String(data.first.servicetype),
args[0], args[1], null, intClient, intPort,
desc, enabled, leaseDuration);
if (ret != MiniupnpcLibrary.UPNPCOMMAND_SUCCESS)
System.out.println("GetSpecificPortMappingEntry() failed with code " + ret);
System.out.println("InternalIP:Port = " +
new String(intClient.array()) + ":" + new String(intPort.array()) +
" (" + new String(desc.array()) + ")");
ret = miniupnpc.UPNP_DeletePortMapping(
urls.controlURL.getString(0),
new String(data.first.servicetype),
args[0], args[1], null);
if (ret != MiniupnpcLibrary.UPNPCOMMAND_SUCCESS)
System.out.println("DelPortMapping() failed with code " + ret);
miniupnpc.FreeUPNPUrls(urls);
} else {
System.out.println("No valid UPNP Internet Gateway Device found.");
}
miniupnpc.freeUPNPDevlist(devlist);
} else {
System.out.println("No IGD UPnP Device found on the network !\n");
}
}
}
@@ -0,0 +1,8 @@
@echo off
set JAVA=java
set JAVAC=javac
REM notice the semicolon for Windows. Write once, run ... oh nevermind
set CP=miniupnpc_win32.jar;.
%JAVAC% -cp "%CP%" JavaBridgeTest.java || exit 1
%JAVA% -cp "%CP%" JavaBridgeTest 12345 UDP || exit 1
@@ -0,0 +1,8 @@
#!/bin/bash
JAVA=java
JAVAC=javac
CP=$(for i in *.jar; do echo -n $i:; done).
$JAVAC -cp $CP JavaBridgeTest.java || exit 1
$JAVA -cp $CP JavaBridgeTest 12345 UDP || exit 1
+198
View File
@@ -0,0 +1,198 @@
/* $Id: listdevices.c,v 1.9 2024/01/26 23:30:47 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2013-2015 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
#include <winsock2.h>
#endif /* _WIN32 */
#include "miniupnpc.h"
struct upnp_dev_list {
struct upnp_dev_list * next;
char * descURL;
struct UPNPDev * * array;
size_t count;
size_t allocated_count;
};
#define ADD_DEVICE_COUNT_STEP 16
void add_device(struct upnp_dev_list * * list_head, struct UPNPDev * dev)
{
struct upnp_dev_list * elt;
size_t i;
if(dev == NULL)
return;
for(elt = *list_head; elt != NULL; elt = elt->next) {
if(strcmp(elt->descURL, dev->descURL) == 0) {
for(i = 0; i < elt->count; i++) {
if (strcmp(elt->array[i]->st, dev->st) == 0 && strcmp(elt->array[i]->usn, dev->usn) == 0) {
return; /* already found */
}
}
if(elt->count >= elt->allocated_count) {
struct UPNPDev * * tmp;
elt->allocated_count += ADD_DEVICE_COUNT_STEP;
tmp = realloc(elt->array, elt->allocated_count * sizeof(struct UPNPDev *));
if(tmp == NULL) {
fprintf(stderr, "Failed to realloc(%p, %lu)\n", elt->array, (unsigned long)(elt->allocated_count * sizeof(struct UPNPDev *)));
return;
}
elt->array = tmp;
}
elt->array[elt->count++] = dev;
return;
}
}
elt = malloc(sizeof(struct upnp_dev_list));
if(elt == NULL) {
fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)sizeof(struct upnp_dev_list));
return;
}
elt->next = *list_head;
elt->descURL = strdup(dev->descURL);
if(elt->descURL == NULL) {
fprintf(stderr, "Failed to strdup(%s)\n", dev->descURL);
free(elt);
return;
}
elt->allocated_count = ADD_DEVICE_COUNT_STEP;
elt->array = malloc(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *));
if(elt->array == NULL) {
fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *)));
free(elt->descURL);
free(elt);
return;
}
elt->array[0] = dev;
elt->count = 1;
*list_head = elt;
}
void free_device(struct upnp_dev_list * elt)
{
free(elt->descURL);
free(elt->array);
free(elt);
}
int main(int argc, char * * argv)
{
const char * searched_device = NULL;
const char * * searched_devices = NULL;
const char * multicastif = 0;
const char * minissdpdpath = 0;
int ipv6 = 0;
unsigned char ttl = 2;
int error = 0;
struct UPNPDev * devlist = 0;
struct UPNPDev * dev;
struct upnp_dev_list * sorted_list = NULL;
struct upnp_dev_list * dev_array;
int i;
#ifdef _WIN32
WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(nResult != NO_ERROR)
{
fprintf(stderr, "WSAStartup() failed.\n");
return -1;
}
#endif
for(i = 1; i < argc; i++) {
if(strcmp(argv[i], "-6") == 0)
ipv6 = 1;
else if(strcmp(argv[i], "-d") == 0) {
if(++i >= argc) {
fprintf(stderr, "%s option needs one argument\n", "-d");
return 1;
}
searched_device = argv[i];
} else if(strcmp(argv[i], "-t") == 0) {
if(++i >= argc) {
fprintf(stderr, "%s option needs one argument\n", "-t");
return 1;
}
ttl = (unsigned char)atoi(argv[i]);
} else if(strcmp(argv[i], "-l") == 0) {
if(++i >= argc) {
fprintf(stderr, "-l option needs at least one argument\n");
return 1;
}
searched_devices = (const char * *)(argv + i);
break;
} else if(strcmp(argv[i], "-m") == 0) {
if(++i >= argc) {
fprintf(stderr, "-m option needs one argument\n");
return 1;
}
multicastif = argv[i];
} else {
printf("usage : %s [options] [-l <device1> <device2> ...]\n", argv[0]);
printf("options :\n");
printf(" -6 : use IPv6\n");
printf(" -m address/ifname : network interface to use for multicast\n");
printf(" -d <device string> : search only for this type of device\n");
printf(" -l <device1> <device2> ... : search only for theses types of device\n");
printf(" -t ttl : set multicast TTL. Default value is 2.\n");
printf(" -h : this help\n");
return 1;
}
}
if(searched_device) {
printf("searching UPnP device type %s\n", searched_device);
devlist = upnpDiscoverDevice(searched_device,
2000, multicastif, minissdpdpath,
0/*localport*/, ipv6, ttl, &error);
} else if(searched_devices) {
printf("searching UPnP device types :\n");
for(i = 0; searched_devices[i]; i++)
printf("\t%s\n", searched_devices[i]);
devlist = upnpDiscoverDevices(searched_devices,
2000, multicastif, minissdpdpath,
0/*localport*/, ipv6, ttl, &error, 1);
} else {
printf("searching all UPnP devices\n");
devlist = upnpDiscoverAll(2000, multicastif, minissdpdpath,
0/*localport*/, ipv6, ttl, &error);
}
if(devlist) {
for(dev = devlist, i = 1; dev != NULL; dev = dev->pNext, i++) {
printf("%3d: %-48s\n", i, dev->st);
printf(" %s\n", dev->descURL);
printf(" %s\n", dev->usn);
add_device(&sorted_list, dev);
}
putchar('\n');
for (dev_array = sorted_list; dev_array != NULL ; dev_array = dev_array->next) {
printf("%s :\n", dev_array->descURL);
for(i = 0; (unsigned)i < dev_array->count; i++) {
printf("%2d: %s\n", i+1, dev_array->array[i]->st);
printf(" %s\n", dev_array->array[i]->usn);
}
putchar('\n');
}
freeUPNPDevlist(devlist);
while(sorted_list != NULL) {
dev_array = sorted_list;
sorted_list = sorted_list->next;
free_device(dev_array);
}
} else {
fprintf(stderr,"No UPnP devices found\n");
return 1;
}
return 0;
}
@@ -0,0 +1,55 @@
.TH MINIUPNPC 3
.SH NAME
miniupnpc \- UPnP client library
.SH SYNOPSIS
.SH DESCRIPTION
The miniupnpc library implement the UPnP protocol defined
to dialog with Internet Gateway Devices. It also has
the ability to use data gathered by minissdpd(1) about
UPnP devices up on the network in order to skip the
long UPnP device discovery process.
.PP
At first, upnpDiscover(3) has to be used to discover UPnP IGD present
on the network. Then UPNP_GetValidIGD(3) to select the right one.
Alternatively, UPNP_GetIGDFromUrl(3) could be used to bypass discovery
process if the root description url of the device to use is known.
Then all the UPNP_* functions can be used, such as
UPNP_GetConnectionTypeInfo(3), UPNP_AddPortMapping(3), etc...
.SH "HEADER FILES"
.IP miniupnpc.h
That's the main header file for the miniupnpc library API.
It contains all the functions and structures related to device discovery.
.IP upnpcommands.h
This header file contain the UPnP IGD methods that are accessible
through the miniupnpc API. The name of the C functions are matching
the UPnP methods names. ie: GetGenericPortMappingEntry is
UPNP_GetGenericPortMappingEntry.
.SH "API FUNCTIONS"
.IP "struct UPNPDev * upnpDiscover(int delay, const char * multicastif, const char * minissdpdsock, int localport, int ipv6, int * error);"
execute the discovery process.
delay (in millisecond) is the maximum time for waiting any device response.
If available, device list will be obtained from MiniSSDPd.
Default path for minissdpd socket will be used if minissdpdsock argument is NULL.
If multicastif is not NULL, it will be used instead of the default multicast interface for sending SSDP discover packets.
If localport is set to UPNP_LOCAL_PORT_SAME(1) SSDP packets will be sent
from the source port 1900 (same as destination port), if set to
UPNP_LOCAL_PORT_ANY(0) system assign a source port, any other value will
be attempted as the source port.
If ipv6 is not 0, IPv6 is used instead of IPv4 for the discovery process.
.IP "void freeUPNPDevlist(struct UPNPDev * devlist);"
free the list returned by upnpDiscover().
.IP "int UPNP_GetValidIGD(struct UPNPDev * devlist, struct UPNPUrls * urls, struct IGDdatas * data, char * lanaddr, int lanaddrlen, char * wanaddr, int wanaddrlen);"
browse the list of device returned by upnpDiscover(), find
a live UPnP internet gateway device and fill structures passed as arguments
with data used for UPNP methods invocation.
.IP "int UPNP_GetIGDFromUrl(const char * rootdescurl, struct UPNPUrls * urls, struct IGDdatas * data, char * lanaddr, int lanaddrlen);"
permit one to bypass the upnpDiscover() call if the xml root description
URL of the UPnP IGD is known.
Fill structures passed as arguments
with data used for UPNP methods invocation.
.IP "void GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, const char *);"
.IP "void FreeUPNPUrls(struct UPNPUrls *);"
.SH "SEE ALSO"
minissdpd(1)
.SH BUGS
@@ -0,0 +1,8 @@
@mingw32-make -f Makefile.mingw %1
@if errorlevel 1 goto end
@if not exist upnpc-static.exe goto end
@strip upnpc-static.exe
@upx --best upnpc-static.exe
@strip upnpc-shared.exe
@upx --best upnpc-shared.exe
:end
@@ -0,0 +1,690 @@
/* $Id: minihttptestserver.c,v 1.25 2020/05/29 21:14:22 nanard Exp $ */
/* Project : miniUPnP
* Author : Thomas Bernard
* Copyright (c) 2011-2018 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#ifndef INADDR_LOOPBACK
#define INADDR_LOOPBACK 0x7f000001
#endif
#define CRAP_LENGTH (2048)
static int server(unsigned short port, const char * expected_file_name, int ipv6);
volatile sig_atomic_t quit = 0;
volatile sig_atomic_t child_to_wait_for = 0;
/**
* signal handler for SIGCHLD (child status has changed)
*/
void handle_signal_chld(int sig)
{
(void)sig;
/* printf("handle_signal_chld(%d)\n", sig); */
++child_to_wait_for;
}
/**
* signal handler for SIGINT (CRTL C)
*/
void handle_signal_int(int sig)
{
(void)sig;
/* printf("handle_signal_int(%d)\n", sig); */
quit = 1;
}
/**
* build a text/plain content of the specified length
*/
void build_content(char * p, size_t n)
{
char line_buffer[80];
int k;
int i = 0;
while(n > 0) {
k = snprintf(line_buffer, sizeof(line_buffer),
"%04d_ABCDEFGHIJKL_This_line_is_64_bytes_long_ABCDEFGHIJKL_%04d\r\n",
i, i);
if(k != 64) {
fprintf(stderr, "snprintf() returned %d in build_content()\n", k);
}
++i;
if(n >= 64) {
memcpy(p, line_buffer, 64);
p += 64;
n -= 64;
} else {
memcpy(p, line_buffer, n);
p += n;
n = 0;
}
}
}
/**
* build crappy content
*/
void build_crap(char * p, size_t n)
{
static const char crap[] = "_CRAP_\r\n";
size_t i;
while(n > 0) {
i = sizeof(crap) - 1;
if(i > n)
i = n;
memcpy(p, crap, i);
p += i;
n -= i;
}
}
/**
* build chunked response.
* return a malloc'ed buffer
*/
char * build_chunked_response(size_t content_length, size_t * response_len)
{
char * response_buffer;
char * content_buffer;
size_t buffer_length;
size_t i;
unsigned int n;
/* allocate to have some margin */
buffer_length = 256 + content_length + (content_length >> 4);
response_buffer = malloc(buffer_length);
if(response_buffer == NULL)
return NULL;
*response_len = snprintf(response_buffer, buffer_length,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n");
/* build the content */
content_buffer = malloc(content_length);
if(content_buffer == NULL) {
free(response_buffer);
return NULL;
}
build_content(content_buffer, content_length);
/* chunk it */
i = 0;
while(i < content_length) {
n = (rand() % 199) + 1;
if(i + n > content_length) {
n = content_length - i;
}
/* TODO : check buffer size ! */
*response_len += snprintf(response_buffer + *response_len,
buffer_length - *response_len,
"%x\r\n", n);
memcpy(response_buffer + *response_len, content_buffer + i, n);
*response_len += n;
i += n;
response_buffer[(*response_len)++] = '\r';
response_buffer[(*response_len)++] = '\n';
}
/* the last chunk : "0\r\n" a empty body and then
* the final "\r\n" */
memcpy(response_buffer + *response_len, "0\r\n\r\n", 5);
*response_len += 5;
free(content_buffer);
printf("resp_length=%lu buffer_length=%lu content_length=%lu\n",
*response_len, buffer_length, content_length);
return response_buffer;
}
/* favicon.ico generator */
#ifdef OLD_HEADER
#define FAVICON_LENGTH (6 + 16 + 12 + 8 + 32 * 4)
#else
#define FAVICON_LENGTH (6 + 16 + 40 + 8 + 32 * 4)
#endif
void build_favicon_content(unsigned char * p, size_t n)
{
int i;
if(n < FAVICON_LENGTH)
return;
/* header : 6 bytes */
*p++ = 0;
*p++ = 0;
*p++ = 1; /* type : ICO */
*p++ = 0;
*p++ = 1; /* number of images in file */
*p++ = 0;
/* image directory (1 entry) : 16 bytes */
*p++ = 16; /* width */
*p++ = 16; /* height */
*p++ = 2; /* number of colors in the palette. 0 = no palette */
*p++ = 0; /* reserved */
*p++ = 1; /* color planes */
*p++ = 0; /* " */
*p++ = 1; /* bpp */
*p++ = 0; /* " */
#ifdef OLD_HEADER
*p++ = 12 + 8 + 32 * 4; /* bmp size */
#else
*p++ = 40 + 8 + 32 * 4; /* bmp size */
#endif
*p++ = 0; /* " */
*p++ = 0; /* " */
*p++ = 0; /* " */
*p++ = 6 + 16; /* bmp offset */
*p++ = 0; /* " */
*p++ = 0; /* " */
*p++ = 0; /* " */
/* BMP */
#ifdef OLD_HEADER
/* BITMAPCOREHEADER */
*p++ = 12; /* size of this header */
*p++ = 0; /* " */
*p++ = 0; /* " */
*p++ = 0; /* " */
*p++ = 16; /* width */
*p++ = 0; /* " */
*p++ = 16 * 2; /* height x 2 ! */
*p++ = 0; /* " */
*p++ = 1; /* color planes */
*p++ = 0; /* " */
*p++ = 1; /* bpp */
*p++ = 0; /* " */
#else
/* BITMAPINFOHEADER */
*p++ = 40; /* size of this header */
*p++ = 0; /* " */
*p++ = 0; /* " */
*p++ = 0; /* " */
*p++ = 16; /* width */
*p++ = 0; /* " */
*p++ = 0; /* " */
*p++ = 0; /* " */
*p++ = 16 * 2; /* height x 2 ! */
*p++ = 0; /* " */
*p++ = 0; /* " */
*p++ = 0; /* " */
*p++ = 1; /* color planes */
*p++ = 0; /* " */
*p++ = 1; /* bpp */
*p++ = 0; /* " */
/* compression method, image size, ppm x, ppm y */
/* colors in the palette ? */
/* important colors */
for(i = 4 * 6; i > 0; --i)
*p++ = 0;
#endif
/* palette */
*p++ = 0; /* b */
*p++ = 0; /* g */
*p++ = 0; /* r */
*p++ = 0; /* reserved */
*p++ = 255; /* b */
*p++ = 255; /* g */
*p++ = 255; /* r */
*p++ = 0; /* reserved */
/* pixel data */
for(i = 16; i > 0; --i) {
if(i & 1) {
*p++ = 0125;
*p++ = 0125;
} else {
*p++ = 0252;
*p++ = 0252;
}
*p++ = 0;
*p++ = 0;
}
/* Opacity MASK */
for(i = 16 * 4; i > 0; --i) {
*p++ = 0;
}
}
enum modes {
MODE_INVALID, MODE_CHUNKED, MODE_ADDCRAP, MODE_NORMAL, MODE_FAVICON, MODE_MALFORMED
};
const struct {
const enum modes mode;
const char * text;
} modes_array[] = {
{MODE_CHUNKED, "chunked"},
{MODE_ADDCRAP, "addcrap"},
{MODE_NORMAL, "normal"},
{MODE_FAVICON, "favicon.ico"},
{MODE_MALFORMED, "malformed"},
{MODE_INVALID, NULL}
};
/**
* write the response with random behaviour !
*/
void send_response(int c, const char * buffer, size_t len)
{
ssize_t n;
while(len > 0) {
n = (rand() % 99) + 1;
if((size_t)n > len)
n = len;
n = write(c, buffer, n);
if(n < 0) {
if(errno != EINTR) {
perror("write");
return;
}
/* if errno == EINTR, try again */
} else {
len -= n;
buffer += n;
usleep(10000); /* 10ms */
}
}
}
/**
* handle the HTTP connection
*/
void handle_http_connection(int c)
{
char request_buffer[2048];
size_t request_len = 0;
int headers_found = 0;
ssize_t n, m;
size_t i;
char request_method[16];
char request_uri[256];
char http_version[16];
char * p;
char * response_buffer;
size_t response_len;
enum modes mode;
size_t content_length = 16*1024;
/* read the request */
while(request_len < sizeof(request_buffer) && !headers_found) {
n = read(c,
request_buffer + request_len,
sizeof(request_buffer) - request_len);
if(n < 0) {
if(errno == EINTR)
continue;
perror("read");
return;
} else if(n==0) {
/* remote host closed the connection */
break;
} else {
request_len += n;
for(i = 0; i < request_len - 3; i++) {
if(0 == memcmp(request_buffer + i, "\r\n\r\n", 4)) {
/* found the end of headers */
headers_found = 1;
break;
}
}
}
}
if(!headers_found) {
/* error */
printf("no HTTP header found in the request\n");
return;
}
printf("headers :\n%.*s", (int)request_len, request_buffer);
/* the request have been received, now parse the request line */
p = request_buffer;
for(i = 0; i < sizeof(request_method) - 1; i++) {
if(*p == ' ' || *p == '\r')
break;
request_method[i] = *p;
++p;
}
request_method[i] = '\0';
while(*p == ' ')
p++;
for(i = 0; i < (int)sizeof(request_uri) - 1; i++) {
if(*p == ' ' || *p == '\r')
break;
request_uri[i] = *p;
++p;
}
request_uri[i] = '\0';
while(*p == ' ')
p++;
for(i = 0; i < (int)sizeof(http_version) - 1; i++) {
if(*p == ' ' || *p == '\r')
break;
http_version[i] = *p;
++p;
}
http_version[i] = '\0';
printf("Method = %s, URI = %s, %s\n",
request_method, request_uri, http_version);
/* check if the request method is allowed */
if(0 != strcmp(request_method, "GET")) {
const char response405[] = "HTTP/1.1 405 Method Not Allowed\r\n"
"Allow: GET\r\n\r\n";
const char * pc;
/* 405 Method Not Allowed */
/* The response MUST include an Allow header containing a list
* of valid methods for the requested resource. */
n = sizeof(response405) - 1;
pc = response405;
while(n > 0) {
m = write(c, pc, n);
if(m<0) {
if(errno != EINTR) {
perror("write");
return;
}
} else {
n -= m;
pc += m;
}
}
return;
}
mode = MODE_INVALID;
/* use the request URI to know what to do */
for(i = 0; modes_array[i].mode != MODE_INVALID; i++) {
if(strstr(request_uri, modes_array[i].text)) {
mode = modes_array[i].mode; /* found */
break;
}
}
switch(mode) {
case MODE_MALFORMED:
response_len = 2048;
response_buffer = malloc(response_len);
if(!response_buffer)
break;
n = snprintf(response_buffer, response_len,
"HTTP/1.1 \r\n"
"\r\n"
/*"0000\r\n"*/);
for (i = n; i < response_len; i++) {
response_buffer[i] = ' ';
}
response_len = n;
break;
case MODE_CHUNKED:
response_buffer = build_chunked_response(content_length, &response_len);
break;
case MODE_ADDCRAP:
response_len = content_length+256;
response_buffer = malloc(response_len);
if(!response_buffer)
break;
n = snprintf(response_buffer, response_len,
"HTTP/1.1 200 OK\r\n"
"Server: minihttptestserver\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: %lu\r\n"
"\r\n", content_length);
response_len = content_length+n+CRAP_LENGTH;
p = realloc(response_buffer, response_len);
if(p == NULL) {
/* error 500 */
free(response_buffer);
response_buffer = NULL;
break;
}
response_buffer = p;
build_content(response_buffer + n, content_length);
build_crap(response_buffer + n + content_length, CRAP_LENGTH);
break;
case MODE_FAVICON:
content_length = FAVICON_LENGTH;
response_len = content_length + 256;
response_buffer = malloc(response_len);
if(!response_buffer)
break;
n = snprintf(response_buffer, response_len,
"HTTP/1.1 200 OK\r\n"
"Server: minihttptestserver\r\n"
"Content-Type: image/vnd.microsoft.icon\r\n"
"Content-Length: %lu\r\n"
"\r\n", content_length);
/* image/x-icon */
build_favicon_content((unsigned char *)(response_buffer + n), content_length);
response_len = content_length + n;
break;
default:
response_len = content_length+256;
response_buffer = malloc(response_len);
if(!response_buffer)
break;
n = snprintf(response_buffer, response_len,
"HTTP/1.1 200 OK\r\n"
"Server: minihttptestserver\r\n"
"Content-Type: text/plain\r\n"
"\r\n");
response_len = content_length+n;
p = realloc(response_buffer, response_len);
if(p == NULL) {
/* Error 500 */
free(response_buffer);
response_buffer = NULL;
break;
}
response_buffer = p;
build_content(response_buffer + n, response_len - n);
}
if(response_buffer) {
send_response(c, response_buffer, response_len);
free(response_buffer);
} else {
/* Error 500 */
}
}
/**
*/
int main(int argc, char * * argv) {
int ipv6 = 0;
int r, i;
unsigned short port = 0;
const char * expected_file_name = NULL;
for(i = 1; i < argc; i++) {
if(argv[i][0] == '-') {
switch(argv[i][1]) {
case '6':
ipv6 = 1;
break;
case 'e':
/* write expected file ! */
expected_file_name = argv[++i];
break;
case 'p':
/* port */
if(++i < argc) {
port = (unsigned short)atoi(argv[i]);
}
break;
default:
fprintf(stderr, "unknown command line switch '%s'\n", argv[i]);
}
} else {
fprintf(stderr, "unknown command line argument '%s'\n", argv[i]);
}
}
srand(time(NULL));
r = server(port, expected_file_name, ipv6);
if(r != 0) {
printf("*** ERROR ***\n");
}
return r;
}
static int server(unsigned short port, const char * expected_file_name, int ipv6)
{
int s, c;
int i;
struct sockaddr_storage server_addr;
socklen_t server_addrlen;
struct sockaddr_storage client_addr;
socklen_t client_addrlen;
pid_t pid;
int child = 0;
int status;
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
/*signal(SIGCHLD, handle_signal_chld);*/
sa.sa_handler = handle_signal_chld;
if(sigaction(SIGCHLD, &sa, NULL) < 0) {
perror("sigaction");
return 1;
}
/*signal(SIGINT, handle_signal_int);*/
sa.sa_handler = handle_signal_int;
if(sigaction(SIGINT, &sa, NULL) < 0) {
perror("sigaction");
return 1;
}
s = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
if(s < 0) {
perror("socket");
return 1;
}
memset(&server_addr, 0, sizeof(struct sockaddr_storage));
memset(&client_addr, 0, sizeof(struct sockaddr_storage));
if(ipv6) {
struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
addr->sin6_family = AF_INET6;
addr->sin6_port = htons(port);
addr->sin6_addr = in6addr_loopback;
} else {
struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
}
if(bind(s, (struct sockaddr *)&server_addr,
ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) < 0) {
perror("bind");
return 1;
}
if(listen(s, 5) < 0) {
perror("listen");
}
if(port == 0) {
server_addrlen = sizeof(struct sockaddr_storage);
if(getsockname(s, (struct sockaddr *)&server_addr, &server_addrlen) < 0) {
perror("getsockname");
return 1;
}
if(ipv6) {
struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
port = ntohs(addr->sin6_port);
} else {
struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
port = ntohs(addr->sin_port);
}
printf("Listening on port %hu\n", port);
fflush(stdout);
}
/* write expected file */
if(expected_file_name) {
FILE * f;
f = fopen(expected_file_name, "wb");
if(f) {
char * buffer;
buffer = malloc(16*1024);
if(buffer == NULL) {
fprintf(stderr, "memory allocation error\n");
} else {
build_content(buffer, 16*1024);
i = fwrite(buffer, 1, 16*1024, f);
if(i != 16*1024) {
fprintf(stderr, "error writing to file %s : %dbytes written (out of %d)\n", expected_file_name, i, 16*1024);
}
free(buffer);
}
fclose(f);
} else {
fprintf(stderr, "error opening file %s for writing\n", expected_file_name);
}
}
/* fork() loop */
while(!child && !quit) {
while(child_to_wait_for > 0) {
pid = wait(&status);
if(pid < 0) {
perror("wait");
} else {
printf("child(%d) terminated with status %d\n", (int)pid, status);
}
--child_to_wait_for;
}
client_addrlen = sizeof(struct sockaddr_storage);
c = accept(s, (struct sockaddr *)&client_addr,
&client_addrlen);
if(c < 0) {
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
continue;
perror("accept");
return 1;
}
printf("accept...\n");
pid = fork();
if(pid < 0) {
perror("fork");
return 1;
} else if(pid == 0) {
/* child */
child = 1;
close(s);
s = -1;
handle_http_connection(c);
}
close(c);
}
if(s >= 0) {
close(s);
s = -1;
}
if(!child) {
while(child_to_wait_for > 0) {
pid = wait(&status);
if(pid < 0) {
perror("wait");
} else {
printf("child(%d) terminated with status %d\n", (int)pid, status);
}
--child_to_wait_for;
}
printf("Bye...\n");
}
return 0;
}
+120
View File
@@ -0,0 +1,120 @@
/* $Id: minisoap.c,v 1.36 2025/05/25 21:56:48 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2005-2025 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
*
* Minimal SOAP implementation for UPnP protocol.
*/
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <io.h>
#include <winsock2.h>
#include "win32_snprintf.h"
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#endif
#include "minisoap.h"
#include "miniupnpcstrings.h"
/* only for malloc */
#include <stdlib.h>
/* httpWrite sends the headers and the body to the socket
* and returns the number of bytes sent */
static int
httpWrite(SOCKET fd, const char * body, int bodysize,
const char * headers, int headerssize)
{
int n = 0;
/*n = write(fd, headers, headerssize);*/
/*if(bodysize>0)
n += write(fd, body, bodysize);*/
/* Note : my old linksys router only took into account
* soap request that are sent into only one packet */
char * p;
/* TODO: AVOID MALLOC, we could use writev() for that */
p = malloc(headerssize+bodysize);
if(!p)
return -1;
memcpy(p, headers, headerssize);
memcpy(p+headerssize, body, bodysize);
/*n = write(fd, p, headerssize+bodysize);*/
n = send(fd, p, headerssize+bodysize, 0);
if(n<0) {
PRINT_SOCKET_ERROR("send");
}
/* disable send on the socket */
/* draytek routers don't seem to like that... */
#if 0
#ifdef _WIN32
if(shutdown(fd, SD_SEND)<0) {
#else
if(shutdown(fd, SHUT_WR)<0) { /*SD_SEND*/
#endif
PRINT_SOCKET_ERROR("shutdown");
}
#endif
free(p);
return n;
}
/* self explanatory */
int soapPostSubmit(SOCKET fd,
const char * url,
const char * host,
unsigned short port,
const char * action,
const char * body,
const char * httpversion)
{
char headerbuf[512];
int headerssize;
char portstr[8];
int bodysize = (int)strlen(body);
/* We are not using keep-alive HTTP connections.
* HTTP/1.1 needs the header Connection: close to do that.
* This is the default with HTTP/1.0
* Using HTTP/1.1 means we need to support chunked transfer-encoding :
* When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked
* transfer encoding. */
/* Connection: close is normally there only in HTTP/1.1 but who knows */
portstr[0] = '\0';
if(port != 80)
sprintf_s(portstr, sizeof(portstr), ":%hu", port);
headerssize = sprintf_s(headerbuf, sizeof(headerbuf),
"POST %s HTTP/%s\r\n"
"Host: %s%s\r\n"
"User-Agent: " OS_STRING " " UPNP_VERSION_STRING " MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
"Content-Length: %d\r\n"
#if (UPNP_VERSION_MAJOR == 1) && (UPNP_VERSION_MINOR == 0)
"Content-Type: text/xml\r\n"
#else
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
#endif
"SOAPAction: \"%s\"\r\n"
"Connection: close\r\n"
"\r\n",
url, httpversion, host, portstr, bodysize, action);
if ((unsigned int)headerssize >= sizeof(headerbuf))
return -1;
#ifdef DEBUG
/*printf("SOAP request : headersize=%d bodysize=%d\n",
headerssize, bodysize);
*/
printf("SOAP request : POST %s HTTP/%s - Host: %s%s\n",
url, httpversion, host, portstr);
printf("SOAPAction: \"%s\" - Content-Length: %d\n", action, bodysize);
printf("Headers :\n%s", headerbuf);
printf("Body :\n%s\n", body);
#endif
return httpWrite(fd, body, bodysize, headerbuf, headerssize);
}
+17
View File
@@ -0,0 +1,17 @@
/* $Id: minisoap.h,v 1.6 2018/04/06 10:53:13 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2005-2018 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
#ifndef MINISOAP_H_INCLUDED
#define MINISOAP_H_INCLUDED
#include "miniupnpc_socketdef.h"
/*int httpWrite(int, const char *, int, const char *);*/
int soapPostSubmit(SOCKET, const char *, const char *, unsigned short,
const char *, const char *, const char *);
#endif
File diff suppressed because it is too large Load Diff
+58
View File
@@ -0,0 +1,58 @@
/* $Id: minissdpc.h,v 1.8 2019/02/10 12:29:23 nanard Exp $ */
/* Project: miniupnp
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author: Thomas Bernard
* Copyright (c) 2005-2015 Thomas Bernard
* This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */
#ifndef MINISSDPC_H_INCLUDED
#define MINISSDPC_H_INCLUDED
#include "miniupnpc_declspec.h"
#include "upnpdev.h"
/* error codes : */
#define MINISSDPC_SUCCESS (0)
#define MINISSDPC_UNKNOWN_ERROR (-1)
#define MINISSDPC_SOCKET_ERROR (-101)
#define MINISSDPC_MEMORY_ERROR (-102)
#define MINISSDPC_INVALID_INPUT (-103)
#define MINISSDPC_INVALID_SERVER_REPLY (-104)
#ifdef __cplusplus
extern "C" {
#endif
#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))
MINIUPNP_LIBSPEC struct UPNPDev *
getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error);
MINIUPNP_LIBSPEC int
connectToMiniSSDPD(const char * socketpath);
MINIUPNP_LIBSPEC int
disconnectFromMiniSSDPD(int s);
MINIUPNP_LIBSPEC int
requestDevicesFromMiniSSDPD(int s, const char * devtype);
MINIUPNP_LIBSPEC struct UPNPDev *
receiveDevicesFromMiniSSDPD(int s, int * error);
#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */
MINIUPNP_LIBSPEC struct UPNPDev *
ssdpDiscoverDevices(const char * const deviceTypes[],
int delay, const char * multicastif,
int localport,
int ipv6, unsigned char ttl,
int * error,
int searchalltypes);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,6 @@
if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/libminiupnpc-shared.cmake" OR MINIUPNPC_USE_STATIC_LIBS)
include("${CMAKE_CURRENT_LIST_DIR}/miniupnpc-private.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/libminiupnpc-static.cmake")
else()
include("${CMAKE_CURRENT_LIST_DIR}/libminiupnpc-shared.cmake")
endif()
+684
View File
@@ -0,0 +1,684 @@
/* $Id: miniupnpc.c,v 1.167 2025/05/25 21:56:48 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas BERNARD
* copyright (c) 2005-2025 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENSE file. */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
/* Win32 Specific includes and defines */
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#include <io.h>
#include <iphlpapi.h>
#include "win32_snprintf.h"
#define strdup _strdup
#ifndef strncasecmp
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
#define strncasecmp _memicmp
#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
#define strncasecmp memicmp
#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
#endif /* #ifndef strncasecmp */
#define MAXHOSTNAMELEN 64
#else /* #ifdef _WIN32 */
/* Standard POSIX includes */
#include <unistd.h>
#if defined(__amigaos__) && !defined(__amigaos4__)
/* Amiga OS 3 specific stuff */
#define socklen_t int
#else
#include <sys/select.h>
#endif
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if.h>
#if !defined(__amigaos__) && !defined(__amigaos4__)
#include <poll.h>
#endif
#include <strings.h>
#include <errno.h>
#define closesocket close
#endif /* #else _WIN32 */
#ifdef __GNU__
#define MAXHOSTNAMELEN 64
#endif
#include "miniupnpc.h"
#include "minissdpc.h"
#include "miniwget.h"
#include "miniwget_private.h"
#include "minisoap.h"
#include "minixml.h"
#include "upnpcommands.h"
#include "connecthostport.h"
#include "addr_is_reserved.h"
/* compare the beginning of a string with a constant string */
#define COMPARE(str, cstr) (0==strncmp(str, cstr, sizeof(cstr) - 1))
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif
#define SOAPPREFIX "s"
#define SERVICEPREFIX "u"
#define SERVICEPREFIX2 'u'
/* root description parsing */
MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
{
struct xmlparser parser;
/* xmlparser object */
parser.xmlstart = buffer;
parser.xmlsize = bufsize;
parser.data = data;
parser.starteltfunc = IGDstartelt;
parser.endeltfunc = IGDendelt;
parser.datafunc = IGDdata;
parser.attfunc = 0;
parsexml(&parser);
#ifdef DEBUG
printIGD(data);
#endif
}
/* simpleUPnPcommand :
* not so simple !
* return values :
* pointer - OK
* NULL - error */
char *
simpleUPnPcommand(const char * url, const char * service,
const char * action, const struct UPNParg * args,
int * bufsize)
{
char hostname[MAXHOSTNAMELEN+1];
unsigned short port = 0;
char * path;
char soapact[128];
char soapbody[2048];
int soapbodylen;
char * buf;
int n;
int status_code;
SOCKET s;
*bufsize = 0;
snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
if(args==NULL)
{
soapbodylen = snprintf(soapbody, sizeof(soapbody),
"<?xml version=\"1.0\"?>\r\n"
"<" SOAPPREFIX ":Envelope "
"xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
"<" SOAPPREFIX ":Body>"
"<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
"</" SERVICEPREFIX ":%s>"
"</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
"\r\n", action, service, action);
if ((unsigned int)soapbodylen >= sizeof(soapbody))
return NULL;
}
else
{
char * p;
const char * pe, * pv;
const char * const pend = soapbody + sizeof(soapbody);
soapbodylen = snprintf(soapbody, sizeof(soapbody),
"<?xml version=\"1.0\"?>\r\n"
"<" SOAPPREFIX ":Envelope "
"xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
"<" SOAPPREFIX ":Body>"
"<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
action, service);
if ((unsigned int)soapbodylen >= sizeof(soapbody))
return NULL;
p = soapbody + soapbodylen;
while(args->elt)
{
if(p >= pend) /* check for space to write next byte */
return NULL;
*(p++) = '<';
pe = args->elt;
while(p < pend && *pe)
*(p++) = *(pe++);
if(p >= pend) /* check for space to write next byte */
return NULL;
*(p++) = '>';
if((pv = args->val))
{
while(p < pend && *pv)
*(p++) = *(pv++);
}
if((p+2) > pend) /* check for space to write next 2 bytes */
return NULL;
*(p++) = '<';
*(p++) = '/';
pe = args->elt;
while(p < pend && *pe)
*(p++) = *(pe++);
if(p >= pend) /* check for space to write next byte */
return NULL;
*(p++) = '>';
args++;
}
if((p+4) > pend) /* check for space to write next 4 bytes */
return NULL;
*(p++) = '<';
*(p++) = '/';
*(p++) = SERVICEPREFIX2;
*(p++) = ':';
pe = action;
while(p < pend && *pe)
*(p++) = *(pe++);
strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
pend - p);
if(soapbody[sizeof(soapbody)-1]) /* strncpy pads buffer with 0s, so if it doesn't end in 0, could not fit full string */
return NULL;
}
if(!parseURL(url, hostname, &port, &path, NULL)) return NULL;
s = connecthostport(hostname, port, 0);
if(ISINVALID(s)) {
/* failed to connect */
return NULL;
}
n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, "1.1");
if(n<=0) {
#ifdef DEBUG
printf("Error sending SOAP request\n");
#endif
closesocket(s);
return NULL;
}
buf = getHTTPResponse(s, bufsize, &status_code);
#ifdef DEBUG
if(*bufsize > 0 && buf)
{
printf("HTTP %d SOAP Response :\n%.*s\n", status_code, *bufsize, buf);
}
else
{
printf("HTTP %d, empty SOAP response. size=%d\n", status_code, *bufsize);
}
#endif
closesocket(s);
return buf;
}
/* upnpDiscoverDevices() :
* return a chained list of all devices found or NULL if
* no devices was found.
* It is up to the caller to free the chained list
* delay is in millisecond (poll).
* UDA v1.1 says :
* The TTL for the IP packet SHOULD default to 2 and
* SHOULD be configurable. */
MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverDevices(const char * const deviceTypes[],
int delay, const char * multicastif,
const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl,
int * error,
int searchalltypes)
{
struct UPNPDev * tmp;
struct UPNPDev * devlist = 0;
#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
int deviceIndex;
#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
if(error)
*error = UPNPDISCOVER_UNKNOWN_ERROR;
#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
/* first try to get infos from minissdpd ! */
if(!minissdpdsock)
minissdpdsock = "/var/run/minissdpd.sock";
if(minissdpdsock[0] != '\0') {
for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
struct UPNPDev * minissdpd_devlist;
int only_rootdevice = 1;
minissdpd_devlist = getDevicesFromMiniSSDPD(deviceTypes[deviceIndex],
minissdpdsock, 0);
if(minissdpd_devlist) {
#ifdef DEBUG
printf("returned by MiniSSDPD: %s\t%s\n",
minissdpd_devlist->st, minissdpd_devlist->descURL);
#endif /* DEBUG */
if(!strstr(minissdpd_devlist->st, "rootdevice"))
only_rootdevice = 0;
for(tmp = minissdpd_devlist; tmp->pNext != NULL; tmp = tmp->pNext) {
#ifdef DEBUG
printf("returned by MiniSSDPD: %s\t%s\n",
tmp->pNext->st, tmp->pNext->descURL);
#endif /* DEBUG */
if(!strstr(tmp->st, "rootdevice"))
only_rootdevice = 0;
}
tmp->pNext = devlist;
devlist = minissdpd_devlist;
if(!searchalltypes && !only_rootdevice)
break;
}
}
}
for(tmp = devlist; tmp != NULL; tmp = tmp->pNext) {
/* We return what we have found if it was not only a rootdevice */
if(!strstr(tmp->st, "rootdevice")) {
if(error)
*error = UPNPDISCOVER_SUCCESS;
return devlist;
}
}
#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
(void)minissdpdsock; /* unused */
#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
/* direct discovery if minissdpd responses are not sufficient */
{
struct UPNPDev * discovered_devlist;
discovered_devlist = ssdpDiscoverDevices(deviceTypes, delay, multicastif, localport,
ipv6, ttl, error, searchalltypes);
if(devlist == NULL)
devlist = discovered_devlist;
else {
for(tmp = devlist; tmp->pNext != NULL; tmp = tmp->pNext);
tmp->pNext = discovered_devlist;
}
}
return devlist;
}
/* upnpDiscover() Discover IGD device */
MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscover(int delay, const char * multicastif,
const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl,
int * error)
{
static const char * const deviceList[] = {
#if 0
"urn:schemas-upnp-org:device:InternetGatewayDevice:2",
"urn:schemas-upnp-org:service:WANIPConnection:2",
#endif
"urn:schemas-upnp-org:device:InternetGatewayDevice:1",
"urn:schemas-upnp-org:service:WANIPConnection:1",
"urn:schemas-upnp-org:service:WANPPPConnection:1",
"upnp:rootdevice",
/*"ssdp:all",*/
0
};
return upnpDiscoverDevices(deviceList,
delay, multicastif, minissdpdsock, localport,
ipv6, ttl, error, 0);
}
/* upnpDiscoverAll() Discover all UPnP devices */
MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverAll(int delay, const char * multicastif,
const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl,
int * error)
{
static const char * const deviceList[] = {
/*"upnp:rootdevice",*/
"ssdp:all",
0
};
return upnpDiscoverDevices(deviceList,
delay, multicastif, minissdpdsock, localport,
ipv6, ttl, error, 0);
}
/* upnpDiscoverDevice() Discover a specific device */
MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl,
int * error)
{
const char * const deviceList[] = {
device,
0
};
return upnpDiscoverDevices(deviceList,
delay, multicastif, minissdpdsock, localport,
ipv6, ttl, error, 0);
}
static char *
build_absolute_url(const char * baseurl, const char * descURL,
const char * url, unsigned int scope_id)
{
size_t l, n;
char * s;
const char * base;
char * p;
#if defined(IF_NAMESIZE) && !defined(_WIN32)
char ifname[IF_NAMESIZE];
#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
char scope_str[8];
#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
if( (url[0] == 'h')
&&(url[1] == 't')
&&(url[2] == 't')
&&(url[3] == 'p')
&&(url[4] == ':')
&&(url[5] == '/')
&&(url[6] == '/'))
return strdup(url);
base = (baseurl[0] == '\0') ? descURL : baseurl;
n = strlen(base);
if(n > 7) {
p = strchr(base + 7, '/');
if(p)
n = p - base;
}
l = n + strlen(url) + 1;
if(url[0] != '/')
l++;
if(scope_id != 0) {
#if defined(IF_NAMESIZE) && !defined(_WIN32)
if(if_indextoname(scope_id, ifname)) {
l += 3 + strlen(ifname); /* 3 == strlen(%25) */
}
#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
/* under windows, scope is numerical */
l += 3 + snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
}
s = malloc(l);
if(s == NULL) return NULL;
memcpy(s, base, n);
if(scope_id != 0) {
s[n] = '\0';
if(n > 13 && 0 == memcmp(s, "http://[fe80:", 13)) {
/* this is a linklocal IPv6 address */
p = strchr(s, ']');
if(p) {
/* insert %25<scope> into URL */
#if defined(IF_NAMESIZE) && !defined(_WIN32)
memmove(p + 3 + strlen(ifname), p, strlen(p) + 1);
memcpy(p, "%25", 3);
memcpy(p + 3, ifname, strlen(ifname));
n += 3 + strlen(ifname);
#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1);
memcpy(p, "%25", 3);
memcpy(p + 3, scope_str, strlen(scope_str));
n += 3 + strlen(scope_str);
#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
}
}
}
if(url[0] != '/')
s[n++] = '/';
memcpy(s + n, url, l - n);
return s;
}
/* Prepare the Urls for usage...
*/
MINIUPNP_LIBSPEC void
GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
const char * descURL, unsigned int scope_id)
{
/* strdup descURL */
urls->rootdescURL = strdup(descURL);
/* get description of WANIPConnection */
urls->ipcondescURL = build_absolute_url(data->urlbase, descURL,
data->first.scpdurl, scope_id);
urls->controlURL = build_absolute_url(data->urlbase, descURL,
data->first.controlurl, scope_id);
urls->controlURL_CIF = build_absolute_url(data->urlbase, descURL,
data->CIF.controlurl, scope_id);
urls->controlURL_6FC = build_absolute_url(data->urlbase, descURL,
data->IPv6FC.controlurl, scope_id);
#ifdef DEBUG
printf("urls->ipcondescURL='%s'\n", urls->ipcondescURL);
printf("urls->controlURL='%s'\n", urls->controlURL);
printf("urls->controlURL_CIF='%s'\n", urls->controlURL_CIF);
printf("urls->controlURL_6FC='%s'\n", urls->controlURL_6FC);
#endif
}
MINIUPNP_LIBSPEC void
FreeUPNPUrls(struct UPNPUrls * urls)
{
if(!urls)
return;
free(urls->controlURL);
urls->controlURL = 0;
free(urls->ipcondescURL);
urls->ipcondescURL = 0;
free(urls->controlURL_CIF);
urls->controlURL_CIF = 0;
free(urls->controlURL_6FC);
urls->controlURL_6FC = 0;
free(urls->rootdescURL);
urls->rootdescURL = 0;
}
int
UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
{
char status[64];
unsigned int uptime;
status[0] = '\0';
UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
status, &uptime, NULL);
if(0 == strcmp("Connected", status))
return 1;
else if(0 == strcmp("Up", status)) /* Also accept "Up" */
return 1;
else
return 0;
}
/* UPNP_GetValidIGD() :
* return values :
* -1 = Internal error
* 0 = NO IGD found (UPNP_NO_IGD)
* 1 = A valid connected IGD has been found (UPNP_CONNECTED_IGD)
* 2 = A valid connected IGD has been found but its
* IP address is reserved (non routable) (UPNP_PRIVATEIP_IGD)
* 3 = A valid IGD has been found but it reported as
* not connected (UPNP_DISCONNECTED_IGD)
* 4 = an UPnP device has been found but was not recognized as an IGD
* (UPNP_UNKNOWN_DEVICE)
*
* In any positive non zero return case, the urls and data structures
* passed as parameters are set. Don't forget to call FreeUPNPUrls(urls) to
* free allocated memory.
*/
MINIUPNP_LIBSPEC int
UPNP_GetValidIGD(struct UPNPDev * devlist,
struct UPNPUrls * urls,
struct IGDdatas * data,
char * lanaddr, int lanaddrlen,
char * wanaddr, int wanaddrlen)
{
struct xml_desc {
char lanaddr[40];
char wanaddr[40];
char * xml;
int size;
int is_igd;
} * desc = NULL;
struct UPNPDev * dev;
int ndev = 0;
int i;
int state = -1; /* state 1 : IGD connected. State 2 : connected with reserved IP.
* State 3 : IGD. State 4 : anything */
int status_code = -1;
if(!devlist)
{
#ifdef DEBUG
printf("Empty devlist\n");
#endif
return 0;
}
/* counting total number of devices in the list */
for(dev = devlist; dev; dev = dev->pNext)
ndev++;
/* ndev is always > 0 */
desc = calloc(ndev, sizeof(struct xml_desc));
if(!desc)
return -1; /* memory allocation error */
/* Step 1 : downloading descriptions and testing type */
for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
{
/* we should choose an internet gateway device.
* with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
desc[i].lanaddr, sizeof(desc[i].lanaddr),
dev->scope_id, &status_code);
#ifdef DEBUG
if(!desc[i].xml)
{
printf("error getting XML description %s\n", dev->descURL);
}
#endif
if(desc[i].xml)
{
memset(data, 0, sizeof(struct IGDdatas));
memset(urls, 0, sizeof(struct UPNPUrls));
parserootdesc(desc[i].xml, desc[i].size, data);
if(COMPARE(data->CIF.servicetype,
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:"))
{
desc[i].is_igd = 1;
}
}
}
/* iterate the list to find a device depending on state */
for(state = 1; state <= 4; state++)
{
for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
{
if(desc[i].xml)
{
memset(data, 0, sizeof(struct IGDdatas));
memset(urls, 0, sizeof(struct UPNPUrls));
parserootdesc(desc[i].xml, desc[i].size, data);
if(desc[i].is_igd || state >= 4 )
{
int is_connected;
GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
/* in state 3 and 4 we don't test if device is connected ! */
if(state >= 3)
goto free_and_return;
is_connected = UPNPIGD_IsConnected(urls, data);
#ifdef DEBUG
printf("UPNPIGD_IsConnected(%s) = %d\n",
urls->controlURL, is_connected);
#endif
/* checks that status is connected AND there is a external IP address assigned */
if(is_connected) {
if(state >= 2)
goto free_and_return;
if(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, desc[i].wanaddr) == 0
&& !addr_is_reserved(desc[i].wanaddr))
goto free_and_return;
}
FreeUPNPUrls(urls);
if(data->second.servicetype[0] != '\0') {
#ifdef DEBUG
printf("We tried %s, now we try %s !\n",
data->first.servicetype, data->second.servicetype);
#endif
/* swaping WANPPPConnection and WANIPConnection ! */
memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
is_connected = UPNPIGD_IsConnected(urls, data);
#ifdef DEBUG
printf("UPNPIGD_IsConnected(%s) = %d\n",
urls->controlURL, is_connected);
#endif
if(is_connected) {
if(state >= 2)
goto free_and_return;
if(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, desc[i].wanaddr) == 0
&& !addr_is_reserved(desc[i].wanaddr))
goto free_and_return;
}
FreeUPNPUrls(urls);
}
}
memset(data, 0, sizeof(struct IGDdatas));
}
}
}
state = 0;
free_and_return:
if (state >= 1 && state <= 4 && i < ndev) {
if (lanaddr != NULL)
strncpy(lanaddr, desc[i].lanaddr, lanaddrlen);
if (wanaddr != NULL)
strncpy(wanaddr, desc[i].wanaddr, wanaddrlen);
}
for(i = 0; i < ndev; i++)
free(desc[i].xml);
free(desc);
return state;
}
/* UPNP_GetIGDFromUrl()
* Used when skipping the discovery process.
* return value :
* 0 - Not ok
* 1 - OK */
int
UPNP_GetIGDFromUrl(const char * rootdescurl,
struct UPNPUrls * urls,
struct IGDdatas * data,
char * lanaddr, int lanaddrlen)
{
char * descXML;
int descXMLsize = 0;
descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
lanaddr, lanaddrlen, 0, NULL);
if(descXML) {
memset(data, 0, sizeof(struct IGDdatas));
memset(urls, 0, sizeof(struct UPNPUrls));
parserootdesc(descXML, descXMLsize, data);
free(descXML);
GetUPNPUrls(urls, data, rootdescurl, 0);
return 1;
} else {
return 0;
}
}
+48
View File
@@ -0,0 +1,48 @@
LIBRARY
; miniupnpc library
miniupnpc
EXPORTS
; miniupnpc
upnpDiscover
upnpDiscoverDevice
upnpDiscoverDevices
upnpDiscoverAll
freeUPNPDevlist
parserootdesc
UPNP_GetValidIGD
UPNP_GetIGDFromUrl
GetUPNPUrls
FreeUPNPUrls
; miniwget
miniwget
miniwget_getaddr
; upnpcommands
UPNP_GetTotalBytesSent
UPNP_GetTotalBytesReceived
UPNP_GetTotalPacketsSent
UPNP_GetTotalPacketsReceived
UPNP_GetStatusInfo
UPNP_GetConnectionTypeInfo
UPNP_GetExternalIPAddress
UPNP_GetLinkLayerMaxBitRates
UPNP_AddPortMapping
UPNP_AddAnyPortMapping
UPNP_DeletePortMapping
UPNP_DeletePortMappingRange
UPNP_GetPortMappingNumberOfEntries
UPNP_GetSpecificPortMappingEntry
UPNP_GetGenericPortMappingEntry
UPNP_GetListOfPortMappings
UPNP_AddPinhole
UPNP_CheckPinholeWorking
UPNP_UpdatePinhole
UPNP_GetPinholePackets
UPNP_DeletePinhole
UPNP_GetFirewallStatus
UPNP_GetOutboundPinholeTimeout
; upnperrors
strupnperror
; portlistingparse
ParsePortListing
FreePortListing
+305
View File
@@ -0,0 +1,305 @@
/* $Id: miniupnpc.h,v 1.79 2025/05/25 21:51:11 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project: miniupnp
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author: Thomas Bernard
* Copyright (c) 2005-2025 Thomas Bernard
* This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */
#ifndef MINIUPNPC_H_INCLUDED
#define MINIUPNPC_H_INCLUDED
/*! \file miniupnpc.h
* \brief Main C API for MiniUPnPc
*
* Contains functions to discover devices and check for device validity
* or connectivity.
*
* \mainpage MiniUPnPc API documentation
* MiniUPnPc (MiniUPnP client) is a library implementing a UPnP
* Internet Gateway Device (IGD) control point.
*
* It should be used by applications that need to listen to incoming
* traffic from the internet which are running on a LAN where a
* UPnP IGD is running on the router (or gateway).
*
* See more documentation on the website http://miniupnp.free.fr
* or https://miniupnp.tuxfamily.org/ or GitHub :
* https://github.com/miniupnp/miniupnp/tree/master/miniupnpc
*/
#include "miniupnpc_declspec.h"
#include "igd_desc_parse.h"
#include "upnpdev.h"
/* error codes : */
/*! \brief value for success */
#define UPNPDISCOVER_SUCCESS (0)
/*! \brief value for unknown error */
#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
/*! \brief value for a socket error */
#define UPNPDISCOVER_SOCKET_ERROR (-101)
/*! \brief value for a memory allocation error */
#define UPNPDISCOVER_MEMORY_ERROR (-102)
/*! \brief software version */
#define MINIUPNPC_VERSION "2.3.3"
/*! \brief C API version */
#define MINIUPNPC_API_VERSION 21
/*! \brief any (ie system chosen) port */
#define UPNP_LOCAL_PORT_ANY 0
/*! \brief Use as an alias for 1900 for backwards compatibility */
#define UPNP_LOCAL_PORT_SAME 1
#ifdef __cplusplus
extern "C" {
#endif
/* Structures definitions : */
/*!
* \brief UPnP method argument
*/
struct UPNParg {
const char * elt; /*!< \brief UPnP argument name */
const char * val; /*!< \brief UPnP argument value */
};
/*!
* \brief execute a UPnP method (SOAP action)
*
* \todo error reporting should be improved
*
* \param[in] url Control URL for the service
* \param[in] service service to use
* \param[in] action action to call
* \param[in] args action arguments
* \param[out] bufsize the size of the returned buffer
* \return NULL in case of error or the raw XML response
*/
char *
simpleUPnPcommand(const char * url, const char * service,
const char * action, const struct UPNParg * args,
int * bufsize);
/*!
* \brief Discover UPnP IGD on the network.
*
* The discovered devices are returned as a chained list.
* It is up to the caller to free the list with freeUPNPDevlist().
* If available, device list will be obtained from MiniSSDPd.
*
* \param[in] delay (in millisecond) maximum time for waiting any device
* response
* \param[in] multicastif If not NULL, used instead of the default
* multicast interface for sending SSDP discover packets
* \param[in] minissdpdsock Path to minissdpd socket, default is used if
* NULL
* \param[in] localport Source port to send SSDP packets.
* #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port)
* #UPNP_LOCAL_PORT_ANY to let system assign a source port
* \param[in] ipv6 0 for IPv4, 1 of IPv6
* \param[in] ttl should default to 2 as advised by UDA 1.1
* \param[out] error error code when NULL is returned
* \return NULL or a linked list
*/
MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscover(int delay, const char * multicastif,
const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl,
int * error);
/*!
* \brief Discover all UPnP devices on the network
*
* search for "ssdp:all"
* \param[in] delay (in millisecond) maximum time for waiting any device
* response
* \param[in] multicastif If not NULL, used instead of the default
* multicast interface for sending SSDP discover packets
* \param[in] minissdpdsock Path to minissdpd socket, default is used if
* NULL
* \param[in] localport Source port to send SSDP packets.
* #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port)
* #UPNP_LOCAL_PORT_ANY to let system assign a source port
* \param[in] ipv6 0 for IPv4, 1 of IPv6
* \param[in] ttl should default to 2 as advised by UDA 1.1
* \param[out] error error code when NULL is returned
* \return NULL or a linked list
*/
MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverAll(int delay, const char * multicastif,
const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl,
int * error);
/*!
* \brief Discover one type of UPnP devices
*
* \param[in] device device type to search
* \param[in] delay (in millisecond) maximum time for waiting any device
* response
* \param[in] multicastif If not NULL, used instead of the default
* multicast interface for sending SSDP discover packets
* \param[in] minissdpdsock Path to minissdpd socket, default is used if
* NULL
* \param[in] localport Source port to send SSDP packets.
* #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port)
* #UPNP_LOCAL_PORT_ANY to let system assign a source port
* \param[in] ipv6 0 for IPv4, 1 of IPv6
* \param[in] ttl should default to 2 as advised by UDA 1.1
* \param[out] error error code when NULL is returned
* \return NULL or a linked list
*/
MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl,
int * error);
/*!
* \brief Discover one or several type of UPnP devices
*
* \param[in] deviceTypes array of device types to search (ending with NULL)
* \param[in] delay (in millisecond) maximum time for waiting any device
* response
* \param[in] multicastif If not NULL, used instead of the default
* multicast interface for sending SSDP discover packets
* \param[in] minissdpdsock Path to minissdpd socket, default is used if
* NULL
* \param[in] localport Source port to send SSDP packets.
* #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port)
* #UPNP_LOCAL_PORT_ANY to let system assign a source port
* \param[in] ipv6 0 for IPv4, 1 of IPv6
* \param[in] ttl should default to 2 as advised by UDA 1.1
* \param[out] error error code when NULL is returned
* \param[in] searchalltypes 0 to stop with the first type returning results
* \return NULL or a linked list
*/
MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverDevices(const char * const deviceTypes[],
int delay, const char * multicastif,
const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl,
int * error,
int searchalltypes);
/*!
* \brief parse root XML description of a UPnP device
*
* fill the IGDdatas structure.
* \param[in] buffer character buffer containing the XML description
* \param[in] bufsize size in bytes of the buffer
* \param[out] data IGDdatas structure to fill
*/
MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data);
/*!
* \brief structure used to get fast access to urls
*/
struct UPNPUrls {
/*! \brief controlURL of the WANIPConnection */
char * controlURL;
/*! \brief url of the description of the WANIPConnection */
char * ipcondescURL;
/*! \brief controlURL of the WANCommonInterfaceConfig */
char * controlURL_CIF;
/*! \brief controlURL of the WANIPv6FirewallControl */
char * controlURL_6FC;
/*! \brief url of the root description */
char * rootdescURL;
};
/*! \brief NO IGD found */
#define UPNP_NO_IGD (0)
/*! \brief valid and connected IGD */
#define UPNP_CONNECTED_IGD (1)
/*! \brief valid and connected IGD but with a reserved address
* (non routable) */
#define UPNP_PRIVATEIP_IGD (2)
/*! \brief valid but not connected IGD */
#define UPNP_DISCONNECTED_IGD (3)
/*! \brief UPnP device not recognized as an IGD */
#define UPNP_UNKNOWN_DEVICE (4)
/*!
* \brief look for a valid and possibly connected IGD in the list
*
* In any non zero return case, the urls and data structures
* passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
* free allocated memory.
* \param[in] devlist A device list obtained with upnpDiscover() /
* upnpDiscoverAll() / upnpDiscoverDevice() / upnpDiscoverDevices()
* \param[out] urls Urls for the IGD found
* \param[out] data datas for the IGD found
* \param[out] lanaddr buffer to copy the local address of the host to reach the IGD
* \param[in] lanaddrlen size of the lanaddr buffer
* \param[out] wanaddr buffer to copy the public address of the IGD
* \param[in] wanaddrlen size of the wanaddr buffer
* \return #UPNP_NO_IGD / #UPNP_CONNECTED_IGD / #UPNP_PRIVATEIP_IGD /
* #UPNP_DISCONNECTED_IGD / #UPNP_UNKNOWN_DEVICE
*/
MINIUPNP_LIBSPEC int
UPNP_GetValidIGD(struct UPNPDev * devlist,
struct UPNPUrls * urls,
struct IGDdatas * data,
char * lanaddr, int lanaddrlen,
char * wanaddr, int wanaddrlen);
/*!
* \brief Get IGD URLs and data for URL
*
* Used when skipping the discovery process.
* \param[in] rootdescurl Root description URL of the device
* \param[out] urls Urls for the IGD found
* \param[out] data datas for the IGD found
* \param[out] lanaddr buffer to copy the local address of the host to reach the IGD
* \param[in] lanaddrlen size of the lanaddr buffer
* \return 0 Not ok / 1 OK
*/
MINIUPNP_LIBSPEC int
UPNP_GetIGDFromUrl(const char * rootdescurl,
struct UPNPUrls * urls,
struct IGDdatas * data,
char * lanaddr, int lanaddrlen);
/*!
* \brief Prepare the URLs for usage
*
* build absolute URLs from the root description
* \param[out] urls URL structure to initialize
* \param[in] data datas for the IGD
* \param[in] descURL root description URL for the IGD
* \param[in] scope_id if not 0, add the scope to the linklocal IPv6
* addresses in URLs
*/
MINIUPNP_LIBSPEC void
GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
const char * descURL, unsigned int scope_id);
/*!
* \brief free the members of a UPNPUrls struct
*
* All URLs buffers are freed and zeroed
* \param[out] urls URL structure to free
*/
MINIUPNP_LIBSPEC void
FreeUPNPUrls(struct UPNPUrls * urls);
/*!
* \brief check the current connection status of an IGD
*
* it uses UPNP_GetStatusInfo()
* \param[in] urls IGD URLs
* \param[in] data IGD data
* \return 1 Connected / 0 Disconnected
*/
MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,18 @@
# this template is filled-in by CMake `configure_file(... @ONLY)`
# the `@....@` are filled in by CMake configure_file(),
# from variables set in your CMakeLists.txt or by CMake itself
#
# Good tutoral for understanding .pc files:
# https://people.freedesktop.org/~dbn/pkg-config-guide.html
prefix="@CMAKE_INSTALL_PREFIX@"
exec_prefix="${prefix}"
libdir="${prefix}/lib"
includedir="${prefix}/include"
Name: @PROJECT_NAME@
Description: @PROJECT_DESCRIPTION@
URL: @PROJECT_HOMEPAGE_URL@
Version: @PROJECT_VERSION@
Libs: -L"${libdir}" -lminiupnpc
Cflags: -I"${includedir}"
+36
View File
@@ -0,0 +1,36 @@
#include <winver.h>
#include "rc_version.h"
VS_VERSION_INFO VERSIONINFO
FILEVERSION LIBMINIUPNPC_MAJOR_VERSION,LIBMINIUPNPC_MINOR_VERSION,LIBMINIUPNPC_MICRO_VERSION,0
PRODUCTVERSION LIBMINIUPNPC_MAJOR_VERSION,LIBMINIUPNPC_MINOR_VERSION,LIBMINIUPNPC_MICRO_VERSION,0
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN // not used
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "04090000" /* Lang = US English, Charset = ASCII */
BEGIN
VALUE "FileDescription", "MiniUPnPc library\0"
VALUE "FileVersion", LIBMINIUPNPC_DOTTED_VERSION "\0"
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", "Copyright (C) Thomas Bernard\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", INTERNAL_NAME
VALUE "ProductName", "MiniUPnPc\0"
VALUE "ProductVersion", LIBMINIUPNPC_DOTTED_VERSION "\0"
VALUE "Comments", "For more information visit https://miniupnp.tuxfamil.org/\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 0 /* US English, ASCII */
END
END
@@ -0,0 +1,22 @@
#ifndef MINIUPNPC_DECLSPEC_H_INCLUDED
#define MINIUPNPC_DECLSPEC_H_INCLUDED
/*! \file miniupnpc_declspec.h
* \brief define #MINIUPNP_LIBSPEC for dll exports and imports */
#if defined(_WIN32) && !defined(MINIUPNP_STATICLIB)
/* for windows dll */
#ifdef MINIUPNP_EXPORTS
#define MINIUPNP_LIBSPEC __declspec(dllexport)
#else
#define MINIUPNP_LIBSPEC __declspec(dllimport)
#endif
#else
#if defined(__GNUC__) && __GNUC__ >= 4
/* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */
#define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default")))
#else
#define MINIUPNP_LIBSPEC
#endif
#endif
#endif /* MINIUPNPC_DECLSPEC_H_INCLUDED */
@@ -0,0 +1,44 @@
/* $Id: miniupnpc_socketdef.h,v 1.4 2021/03/02 23:35:29 nanard Exp $ */
/* Miniupnp project : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
* Copyright (c) 2018 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided within this distribution */
#ifndef MINIUPNPC_SOCKETDEF_H_INCLUDED
#define MINIUPNPC_SOCKETDEF_H_INCLUDED
#ifdef _WIN32
#define ISINVALID(s) (INVALID_SOCKET==(s))
#else
#ifndef SOCKET
#define SOCKET int
#endif
#ifndef SSIZE_T
#define SSIZE_T ssize_t
#endif
#ifndef INVALID_SOCKET
#define INVALID_SOCKET (-1)
#endif
#ifndef ISINVALID
#define ISINVALID(s) ((s)<0)
#endif
#endif
#ifdef _MSC_VER
#define MSC_CAST_INT (int)
#else
#define MSC_CAST_INT
#endif
/* definition of PRINT_SOCKET_ERROR */
#ifdef _WIN32
#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
#else
#define PRINT_SOCKET_ERROR(x) perror(x)
#endif
#endif /* MINIUPNPC_SOCKETDEF_H_INCLUDED */
@@ -0,0 +1,743 @@
/* $Id: miniupnpcmodule.c,v 1.40 2024/05/09 15:10:29 nanard Exp $*/
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Author : Thomas BERNARD
* website : https://miniupnp.tuxfamily.org/
* copyright (c) 2007-2024 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
#include <Python.h>
#define MINIUPNP_STATICLIB
#include <structmember.h>
#include "miniupnpc.h"
#include "upnpcommands.h"
#include "upnperrors.h"
#ifdef _WIN32
#include <winsock2.h>
#endif
/* for compatibility with Python < 2.4 */
#ifndef Py_RETURN_NONE
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
#endif
#ifndef Py_RETURN_TRUE
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
#endif
#ifndef Py_RETURN_FALSE
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
#endif
/* for compatibility with Python < 3.0 */
#ifndef PyVarObject_HEAD_INIT
#define PyVarObject_HEAD_INIT(type, size) \
PyObject_HEAD_INIT(type) size,
#endif
#ifndef Py_TYPE
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
#endif
typedef struct {
PyObject_HEAD
/* Type-specific fields go here. */
struct UPNPDev * devlist;
struct UPNPUrls urls;
struct IGDdatas data;
unsigned int discoverdelay; /* value passed to upnpDiscover() */
unsigned int localport; /* value passed to upnpDiscover() */
char lanaddr[40]; /* our ip address on the LAN */
char wanaddr[40]; /* the ExternalIPAddress returned by the IGD */
char * multicastif;
char * minissdpdsocket;
} UPnPObject;
static PyMemberDef UPnP_members[] = {
{"lanaddr", T_STRING_INPLACE, offsetof(UPnPObject, lanaddr),
READONLY, "ip address on the LAN"
},
{"wanaddr", T_STRING_INPLACE, offsetof(UPnPObject, wanaddr),
READONLY, "public ip address on the WAN"
},
{"discoverdelay", T_UINT, offsetof(UPnPObject, discoverdelay),
0/*READWRITE*/, "value in ms used to wait for SSDP responses"
},
{"localport", T_UINT, offsetof(UPnPObject, localport),
0/*READWRITE*/,
"If localport is set to UPNP_LOCAL_PORT_SAME(1) "
"SSDP packets will be sent from the source port "
"1900 (same as destination port), if set to "
"UPNP_LOCAL_PORT_ANY(0) system assign a source "
"port, any other value will be attempted as the "
"source port"
},
/* T_STRING is always readonly :( */
{"multicastif", T_STRING, offsetof(UPnPObject, multicastif),
0, "IP of the network interface to be used for multicast operations"
},
{"minissdpdsocket", T_STRING, offsetof(UPnPObject, minissdpdsocket),
0, "path of the MiniSSDPd unix socket"
},
{NULL}
};
static int UPnP_init(UPnPObject *self, PyObject *args, PyObject *kwds)
{
char* multicastif = NULL;
char* minissdpdsocket = NULL;
static char *kwlist[] = {
"multicastif", "minissdpdsocket", "discoverdelay",
"localport", NULL
};
if(!PyArg_ParseTupleAndKeywords(args, kwds, "|zzII", kwlist,
&multicastif,
&minissdpdsocket,
&self->discoverdelay,
&self->localport))
return -1;
if(self->localport>1 &&
(self->localport>65534||self->localport<1024)) {
PyErr_SetString(PyExc_Exception, "Invalid localport value");
return -1;
}
if(multicastif)
self->multicastif = strdup(multicastif);
if(minissdpdsocket)
self->minissdpdsocket = strdup(minissdpdsocket);
return 0;
}
static void
UPnPObject_dealloc(UPnPObject *self)
{
freeUPNPDevlist(self->devlist);
FreeUPNPUrls(&self->urls);
free(self->multicastif);
free(self->minissdpdsocket);
Py_TYPE(self)->tp_free((PyObject*)self);
}
static PyObject *
UPnP_discover(UPnPObject *self)
{
int error = 0;
PyObject *res = NULL;
if(self->devlist)
{
freeUPNPDevlist(self->devlist);
self->devlist = 0;
}
Py_BEGIN_ALLOW_THREADS
self->devlist = upnpDiscover((int)self->discoverdelay/*timeout in ms*/,
self->multicastif,
self->minissdpdsocket,
(int)self->localport,
0/*ip v6*/,
2/* TTL */,
&error);
Py_END_ALLOW_THREADS
/* Py_RETURN_NONE ??? */
if (self->devlist != NULL) {
struct UPNPDev * dev;
int i = 0;
for(dev = self->devlist; dev; dev = dev->pNext)
i++;
res = Py_BuildValue("i", i);
return res;
} else {
PyErr_SetString(PyExc_Exception, strupnperror(error));
return NULL;
}
}
static PyObject *
UPnP_selectigd(UPnPObject *self, PyObject *args)
{
const char * rootDescUrl = NULL;
int r;
if(!PyArg_ParseTuple(args, "|z", &rootDescUrl))
return NULL;
Py_BEGIN_ALLOW_THREADS
if (rootDescUrl == NULL) {
r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data,
self->lanaddr, sizeof(self->lanaddr),
self->wanaddr, sizeof(self->wanaddr));
} else {
r = UPNP_GetIGDFromUrl(rootDescUrl, &self->urls, &self->data,
self->lanaddr, sizeof(self->lanaddr));
}
Py_END_ALLOW_THREADS
if(r)
{
return Py_BuildValue("s", self->urls.controlURL);
}
else
{
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, "No UPnP device discovered");
return NULL;
}
}
static PyObject *
UPnP_totalbytesent(UPnPObject *self)
{
UNSIGNED_INTEGER i;
Py_BEGIN_ALLOW_THREADS
i = UPNP_GetTotalBytesSent(self->urls.controlURL_CIF,
self->data.CIF.servicetype);
Py_END_ALLOW_THREADS
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", i);
#else
return Py_BuildValue("i", (int)i);
#endif
}
static PyObject *
UPnP_totalbytereceived(UPnPObject *self)
{
UNSIGNED_INTEGER i;
Py_BEGIN_ALLOW_THREADS
i = UPNP_GetTotalBytesReceived(self->urls.controlURL_CIF,
self->data.CIF.servicetype);
Py_END_ALLOW_THREADS
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", i);
#else
return Py_BuildValue("i", (int)i);
#endif
}
static PyObject *
UPnP_totalpacketsent(UPnPObject *self)
{
UNSIGNED_INTEGER i;
Py_BEGIN_ALLOW_THREADS
i = UPNP_GetTotalPacketsSent(self->urls.controlURL_CIF,
self->data.CIF.servicetype);
Py_END_ALLOW_THREADS
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", i);
#else
return Py_BuildValue("i", (int)i);
#endif
}
static PyObject *
UPnP_totalpacketreceived(UPnPObject *self)
{
UNSIGNED_INTEGER i;
Py_BEGIN_ALLOW_THREADS
i = UPNP_GetTotalPacketsReceived(self->urls.controlURL_CIF,
self->data.CIF.servicetype);
Py_END_ALLOW_THREADS
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", i);
#else
return Py_BuildValue("i", (int)i);
#endif
}
static PyObject *
UPnP_statusinfo(UPnPObject *self)
{
char status[64];
char lastconnerror[64];
unsigned int uptime = 0;
int r;
status[0] = '\0';
lastconnerror[0] = '\0';
Py_BEGIN_ALLOW_THREADS
r = UPNP_GetStatusInfo(self->urls.controlURL, self->data.first.servicetype,
status, &uptime, lastconnerror);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("(s,I,s)", status, uptime, lastconnerror);
#else
return Py_BuildValue("(s,i,s)", status, (int)uptime, lastconnerror);
#endif
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
static PyObject *
UPnP_connectiontype(UPnPObject *self)
{
char connectionType[64];
int r;
connectionType[0] = '\0';
Py_BEGIN_ALLOW_THREADS
r = UPNP_GetConnectionTypeInfo(self->urls.controlURL,
self->data.first.servicetype,
connectionType);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
return Py_BuildValue("s", connectionType);
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
static PyObject *
UPnP_externalipaddress(UPnPObject *self)
{
char externalIPAddress[40];
int r;
externalIPAddress[0] = '\0';
Py_BEGIN_ALLOW_THREADS
r = UPNP_GetExternalIPAddress(self->urls.controlURL,
self->data.first.servicetype,
externalIPAddress);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
return Py_BuildValue("s", externalIPAddress);
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
/* AddPortMapping(externalPort, protocol, internalHost, internalPort, desc,
* remoteHost, leaseDuration)
* protocol is 'UDP' or 'TCP' */
static PyObject *
UPnP_addportmapping(UPnPObject *self, PyObject *args)
{
char extPort[6];
unsigned short ePort;
char inPort[6];
unsigned short iPort;
const char * proto;
const char * host;
const char * desc;
const char * remoteHost;
unsigned int intLeaseDuration = 0;
char strLeaseDuration[12];
int r;
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
if (!PyArg_ParseTuple(args, "HssHzz|I", &ePort, &proto,
&host, &iPort, &desc, &remoteHost, &intLeaseDuration))
#else
if (!PyArg_ParseTuple(args, "HssHzz|i", &ePort, &proto,
&host, &iPort, &desc, &remoteHost, (int *)&intLeaseDuration))
#endif
return NULL;
Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort);
sprintf(inPort, "%hu", iPort);
sprintf(strLeaseDuration, "%u", intLeaseDuration);
r = UPNP_AddPortMapping(self->urls.controlURL, self->data.first.servicetype,
extPort, inPort, host, desc, proto,
remoteHost, strLeaseDuration);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS)
{
Py_RETURN_TRUE;
}
else
{
// TODO: RAISE an Exception. See upnpcommands.h for errors codes.
// upnperrors.c
//Py_RETURN_FALSE;
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
/* AddAnyPortMapping(externalPort, protocol, internalHost, internalPort, desc,
* remoteHost)
* protocol is 'UDP' or 'TCP' */
static PyObject *
UPnP_addanyportmapping(UPnPObject *self, PyObject *args)
{
char extPort[6];
unsigned short ePort;
char inPort[6];
unsigned short iPort;
char reservedPort[6];
const char * proto;
const char * host;
const char * desc;
const char * remoteHost;
const char * leaseDuration = "0";
int r;
if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
return NULL;
Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort);
sprintf(inPort, "%hu", iPort);
r = UPNP_AddAnyPortMapping(self->urls.controlURL, self->data.first.servicetype,
extPort, inPort, host, desc, proto,
remoteHost, leaseDuration, reservedPort);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
return Py_BuildValue("i", atoi(reservedPort));
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
/* DeletePortMapping(extPort, proto, removeHost='')
* proto = 'UDP', 'TCP' */
static PyObject *
UPnP_deleteportmapping(UPnPObject *self, PyObject *args)
{
char extPort[6];
unsigned short ePort;
const char * proto;
const char * remoteHost = "";
int r;
if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
return NULL;
Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort);
r = UPNP_DeletePortMapping(self->urls.controlURL, self->data.first.servicetype,
extPort, proto, remoteHost);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
Py_RETURN_TRUE;
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
/* DeletePortMappingRange(extPort, proto, removeHost='')
* proto = 'UDP', 'TCP' */
static PyObject *
UPnP_deleteportmappingrange(UPnPObject *self, PyObject *args)
{
char extPortStart[6];
unsigned short ePortStart;
char extPortEnd[6];
unsigned short ePortEnd;
const char * proto;
unsigned char manage;
char manageStr[6];
int r;
if(!PyArg_ParseTuple(args, "HHsb", &ePortStart, &ePortEnd, &proto, &manage))
return NULL;
Py_BEGIN_ALLOW_THREADS
sprintf(extPortStart, "%hu", ePortStart);
sprintf(extPortEnd, "%hu", ePortEnd);
sprintf(manageStr, "%hu", (unsigned short)manage);
r = UPNP_DeletePortMappingRange(self->urls.controlURL, self->data.first.servicetype,
extPortStart, extPortEnd, proto, manageStr);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
Py_RETURN_TRUE;
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
static PyObject *
UPnP_getportmappingnumberofentries(UPnPObject *self)
{
unsigned int n = 0;
int r;
Py_BEGIN_ALLOW_THREADS
r = UPNP_GetPortMappingNumberOfEntries(self->urls.controlURL,
self->data.first.servicetype,
&n);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", n);
#else
return Py_BuildValue("i", (int)n);
#endif
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
/* GetSpecificPortMapping(ePort, proto, remoteHost='')
* proto = 'UDP' or 'TCP' */
static PyObject *
UPnP_getspecificportmapping(UPnPObject *self, PyObject *args)
{
char extPort[6];
unsigned short ePort;
const char * proto;
const char * remoteHost = "";
char intClient[40];
char intPort[6];
unsigned short iPort;
char desc[80];
char enabled[4];
char leaseDuration[16];
if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
return NULL;
extPort[0] = '\0'; intClient[0] = '\0'; intPort[0] = '\0';
desc[0] = '\0'; enabled[0] = '\0'; leaseDuration[0] = '\0';
Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort);
UPNP_GetSpecificPortMappingEntry(self->urls.controlURL,
self->data.first.servicetype,
extPort, proto, remoteHost,
intClient, intPort,
desc, enabled, leaseDuration);
Py_END_ALLOW_THREADS
if(intClient[0])
{
iPort = (unsigned short)atoi(intPort);
return Py_BuildValue("(s,H,s,O,i)",
intClient, iPort, desc,
PyBool_FromLong(atoi(enabled)),
atoi(leaseDuration));
}
else
{
Py_RETURN_NONE;
}
}
/* GetGenericPortMapping(index) */
static PyObject *
UPnP_getgenericportmapping(UPnPObject *self, PyObject *args)
{
int i, r;
char index[8];
char intClient[40];
char intPort[6];
unsigned short iPort;
char extPort[6];
unsigned short ePort;
char protocol[4];
char desc[80];
char enabled[6];
char rHost[64];
char duration[16]; /* lease duration */
unsigned int dur;
if(!PyArg_ParseTuple(args, "i", &i))
return NULL;
Py_BEGIN_ALLOW_THREADS
snprintf(index, sizeof(index), "%d", i);
rHost[0] = '\0'; enabled[0] = '\0';
duration[0] = '\0'; desc[0] = '\0';
extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
r = UPNP_GetGenericPortMappingEntry(self->urls.controlURL,
self->data.first.servicetype,
index,
extPort, intClient, intPort,
protocol, desc, enabled, rHost,
duration);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS)
{
ePort = (unsigned short)atoi(extPort);
iPort = (unsigned short)atoi(intPort);
dur = (unsigned int)strtoul(duration, 0, 0);
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("(H,s,(s,H),s,s,s,I)",
ePort, protocol, intClient, iPort,
desc, enabled, rHost, dur);
#else
return Py_BuildValue("(i,s,(s,i),s,s,s,i)",
(int)ePort, protocol, intClient, (int)iPort,
desc, enabled, rHost, (int)dur);
#endif
}
else
{
Py_RETURN_NONE;
}
}
/* miniupnpc.UPnP object Method Table */
static PyMethodDef UPnP_methods[] = {
{"discover", (PyCFunction)UPnP_discover, METH_NOARGS,
"discover UPnP IGD devices on the network"
},
{"selectigd", (PyCFunction)UPnP_selectigd, METH_VARARGS,
"select a valid UPnP IGD among discovered devices"
},
{"totalbytesent", (PyCFunction)UPnP_totalbytesent, METH_NOARGS,
"return the total number of bytes sent by UPnP IGD"
},
{"totalbytereceived", (PyCFunction)UPnP_totalbytereceived, METH_NOARGS,
"return the total number of bytes received by UPnP IGD"
},
{"totalpacketsent", (PyCFunction)UPnP_totalpacketsent, METH_NOARGS,
"return the total number of packets sent by UPnP IGD"
},
{"totalpacketreceived", (PyCFunction)UPnP_totalpacketreceived, METH_NOARGS,
"return the total number of packets received by UPnP IGD"
},
{"statusinfo", (PyCFunction)UPnP_statusinfo, METH_NOARGS,
"return status and uptime"
},
{"connectiontype", (PyCFunction)UPnP_connectiontype, METH_NOARGS,
"return IGD WAN connection type"
},
{"externalipaddress", (PyCFunction)UPnP_externalipaddress, METH_NOARGS,
"return external IP address"
},
{"addportmapping", (PyCFunction)UPnP_addportmapping, METH_VARARGS,
"add a port mapping"
},
{"addanyportmapping", (PyCFunction)UPnP_addanyportmapping, METH_VARARGS,
"add a port mapping, IGD to select alternative if necessary"
},
{"deleteportmapping", (PyCFunction)UPnP_deleteportmapping, METH_VARARGS,
"delete a port mapping"
},
{"deleteportmappingrange", (PyCFunction)UPnP_deleteportmappingrange, METH_VARARGS,
"delete a range of port mappings"
},
{"getportmappingnumberofentries", (PyCFunction)UPnP_getportmappingnumberofentries, METH_NOARGS,
"-- non standard --"
},
{"getspecificportmapping", (PyCFunction)UPnP_getspecificportmapping, METH_VARARGS,
"get details about a specific port mapping entry"
},
{"getgenericportmapping", (PyCFunction)UPnP_getgenericportmapping, METH_VARARGS,
"get all details about the port mapping at index"
},
{NULL} /* Sentinel */
};
static PyTypeObject UPnPType = {
PyVarObject_HEAD_INIT(NULL,
0) /*ob_size*/
"miniupnpc.UPnP", /*tp_name*/
sizeof(UPnPObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)UPnPObject_dealloc,/*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"UPnP objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
UPnP_methods, /* tp_methods */
UPnP_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)UPnP_init, /* tp_init */
0, /* tp_alloc */
#ifndef _WIN32
PyType_GenericNew,/*UPnP_new,*/ /* tp_new */
#else
0, /* tp_new */
#endif
0, /* tp_free */
};
/* module methods */
static PyMethodDef miniupnpc_methods[] = {
{NULL} /* Sentinel */
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"miniupnpc", /* m_name */
"miniupnpc module.", /* m_doc */
-1, /* m_size */
miniupnpc_methods, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
#endif
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
#if PY_MAJOR_VERSION >= 3
PyInit_miniupnpc(void)
#else
initminiupnpc(void)
#endif
{
PyObject* m;
#ifdef _WIN32
/* initialize Winsock. */
WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (nResult != 0)
{
/* error code could be WSASYSNOTREADY WSASYSNOTREADY
* WSASYSNOTREADY WSASYSNOTREADY WSASYSNOTREADY */
#if PY_MAJOR_VERSION >= 3
return 0;
#else
return;
#endif
}
UPnPType.tp_new = PyType_GenericNew;
#endif
if (PyType_Ready(&UPnPType) < 0)
#if PY_MAJOR_VERSION >= 3
return 0;
#else
return;
#endif
#if PY_MAJOR_VERSION >= 3
m = PyModule_Create(&moduledef);
#else
m = Py_InitModule3("miniupnpc", miniupnpc_methods,
"miniupnpc module.");
#endif
Py_INCREF(&UPnPType);
PyModule_AddObject(m, "UPnP", (PyObject *)&UPnPType);
#if PY_MAJOR_VERSION >= 3
return m;
#endif
}
@@ -0,0 +1,8 @@
#ifndef MINIUPNPCSTRINGS_H_INCLUDED
#define MINIUPNPCSTRINGS_H_INCLUDED
#define OS_STRING "MSWindows"
#define MINIUPNPC_VERSION_STRING "2.3.3"
#define UPNP_VERSION_STRING "2.3.3"
#endif
@@ -0,0 +1,19 @@
#ifndef MINIUPNPCSTRINGS_H_INCLUDED
#define MINIUPNPCSTRINGS_H_INCLUDED
#define OS_STRING "${CMAKE_SYSTEM_NAME}"
#define MINIUPNPC_VERSION_STRING "${PROJECT_VERSION}"
#if 0
/* according to "UPnP Device Architecture 1.0" */
#define UPNP_VERSION_MAJOR 1
#define UPNP_VERSION_MINOR 0
#define UPNP_VERSION_STRING "UPnP/1.0"
#else
/* according to "UPnP Device Architecture 1.1" */
#define UPNP_VERSION_MAJOR 1
#define UPNP_VERSION_MINOR 1
#define UPNP_VERSION_STRING "UPnP/1.1"
#endif
#endif
@@ -0,0 +1,27 @@
/* $Id: miniupnpcstrings.h.in,v 1.7 2023/07/05 22:43:50 nanard Exp $ */
/* Project: miniupnp
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author: Thomas Bernard
* Copyright (c) 2005-2014 Thomas Bernard
* This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */
#ifndef MINIUPNPCSTRINGS_H_INCLUDED
#define MINIUPNPCSTRINGS_H_INCLUDED
#define OS_STRING "OS/version"
#define MINIUPNPC_VERSION_STRING "version"
#if 0
/* according to "UPnP Device Architecture 1.0" */
#define UPNP_VERSION_MAJOR 1
#define UPNP_VERSION_MINOR 0
#define UPNP_VERSION_STRING "UPnP/1.0"
#else
/* according to "UPnP Device Architecture 1.1" */
#define UPNP_VERSION_MAJOR 1
#define UPNP_VERSION_MINOR 1
#define UPNP_VERSION_STRING "UPnP/1.1"
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More