upnp & discord game invite support (very buggy)

This commit is contained in:
LazyByteDev
2026-03-04 10:11:27 -05:00
parent da1b5528ee
commit e36bcb6ef0
150 changed files with 27271 additions and 51 deletions

View File

@@ -201,13 +201,16 @@ 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;

View File

@@ -7,6 +7,7 @@
#include "..\..\Windows64\Network\WinsockNetLayer.h"
#include "..\..\Minecraft.h"
#include "..\..\User.h"
#include "..\..\x64\Extrax64Stubs.h"
#endif
CPlatformNetworkManagerStub *g_pPlatformNetworkManager;

View File

@@ -62,9 +62,9 @@ private:
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

View File

@@ -1,4 +1,5 @@
#include "stdafx.h"
#include "x64\Extrax64Stubs.h"
#ifndef __PS3__
//#include <compressapi.h>
#endif // __PS3__
@@ -73,6 +74,79 @@ 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)
{
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;
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)
{
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)
{
});
}
}
static void OnActivityJoinRequest(void* event_data, struct DiscordUser* user)
{
struct IDiscordActivityManager* am = discordCore->get_activity_manager(discordCore);
if(am != NULL)
{
static int s_dummy = 0;
}
}
void Discord_SetJoinSecret(const char* hostIp, WORD port)
{
sprintf_s(s_DiscordJoinSecret, sizeof(s_DiscordJoinSecret), "%s:%u", hostIp, (unsigned)port);
ProfileManager.SetCurrentGameActivity(0,4,false);
}
static void InitDiscordCore()
{
@@ -81,19 +155,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)
@@ -109,9 +195,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)
{
@@ -872,6 +955,19 @@ 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);

View File

@@ -1297,6 +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</AdditionalDependencies>
<ShowProgress>NotSet</ShowProgress>
<SuppressStartupBanner>false</SuppressStartupBanner>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<ImageXex>
<ConfigurationFile>$(ProjectDir)xbox\xex-dev.xml</ConfigurationFile>
@@ -1402,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>
@@ -1410,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>
@@ -1422,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>
@@ -2937,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>
@@ -4362,6 +4365,7 @@ 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" />
@@ -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>
@@ -16714,6 +16687,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<ClInclude Include="WolfModel.h" />
<ClInclude Include="WolfRenderer.h" />
<ClInclude Include="WstringLookup.h" />
<ClInclude Include="x64\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>
@@ -21565,6 +21539,19 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<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">

View File

@@ -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>
@@ -3662,7 +3661,13 @@
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Windows64_Minecraft.h">
<Filter>Windows64\Source Files</Filter>
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="x64\Extrax64Stubs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="miniupnpc\miniupnpcstrings.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
@@ -5091,9 +5096,6 @@
<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>
@@ -5697,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">

View File

@@ -1,12 +1,15 @@
// Code implemented by LCEMP, credit if used on other repos
#include "stdafx.h"
#ifdef _WINDOWS64
#include "stubs.h"
#include "WinsockNetLayer.h"
#include "..\..\Common\Network\PlatformNetworkManagerStub.h"
#include "..\..\..\Minecraft.World\Socket.h"
#include "x64\Extrax64Stubs.h"
#include "miniupnpc/miniupnpc.h"
#include "miniupnpc/upnpcommands.h"
SOCKET WinsockNetLayer::s_listenSocket = INVALID_SOCKET;
SOCKET WinsockNetLayer::s_hostConnectionSocket = INVALID_SOCKET;
@@ -51,6 +54,9 @@ bool g_Win64MultiplayerJoin = false;
int g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT;
char g_Win64MultiplayerIP[256] = "127.0.0.1";
bool WinsockNetLayer::s_upnpMapped = false;
char WinsockNetLayer::s_externalIP[64] = "";
bool WinsockNetLayer::Initialize()
{
if (s_initialized) return true;
@@ -77,8 +83,35 @@ bool WinsockNetLayer::Initialize()
return true;
}
void WinsockNetLayer::CleanupUPnP(int port)
{
if (!s_upnpMapped) return;
struct UPNPDev* devlist = NULL;
int error = 0;
devlist = upnpDiscover(2000, NULL, NULL, UPNP_LOCAL_PORT_ANY, 0, 2, &error);
if (devlist == NULL) return;
struct UPNPUrls urls;
struct IGDdatas data;
char lanAddr[64] = "";
char wanAddr[64] = "";
int status = UPNP_GetValidIGD(devlist, &urls, &data, lanAddr, sizeof(lanAddr), wanAddr, sizeof(wanAddr));
freeUPNPDevlist(devlist);
if (status != 1) return;
char portStr[16];
sprintf_s(portStr, "%d", port);
UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, portStr, "TCP", NULL);
FreeUPNPUrls(&urls);
app.DebugPrintf("UPnP: Port %d unmapped\n", port);
s_upnpMapped = false;
}
void WinsockNetLayer::Shutdown()
{
CleanupUPnP(s_hostGamePort);
StopAdvertising();
StopDiscovery();
@@ -138,6 +171,58 @@ void WinsockNetLayer::Shutdown()
}
}
static bool TryUPnP(int port, char* externalIPOut)
{
struct UPNPDev* devlist = NULL;
char lanAddr[64] = "";
int error = 0;
devlist = upnpDiscover(2000, NULL, NULL, UPNP_LOCAL_PORT_ANY, 0, 2, &error);
if (devlist == NULL)
{
app.DebugPrintf("UPnP: No devices found (error %d)\n", error);
return false;
}
struct UPNPUrls urls;
struct IGDdatas data;
char wanAddr[64] = "";
int status = UPNP_GetValidIGD(devlist, &urls, &data, lanAddr, sizeof(lanAddr), wanAddr, sizeof(wanAddr));
freeUPNPDevlist(devlist);
if (status != UPNP_CONNECTED_IGD)
{
app.DebugPrintf("UPnP: No valid IGD found (status %d)\n", status);
return false;
}
strncpy_s(externalIPOut, 64, wanAddr, _TRUNCATE);
app.DebugPrintf("UPnP: External IP: %s, LAN IP: %s\n", externalIPOut, lanAddr);
app.DebugPrintf("UPnP: External IP: %s, LAN IP: %s\n", externalIPOut, lanAddr);
// Map the port
char portStr[16];
sprintf_s(portStr, "%d", port);
int r = UPNP_AddPortMapping(
urls.controlURL, data.first.servicetype,
portStr, portStr, lanAddr,
"Minecraft LAN", "TCP", NULL, "86400"
);
FreeUPNPUrls(&urls);
if (r != UPNPCOMMAND_SUCCESS)
{
app.DebugPrintf("UPnP: AddPortMapping failed (error %d)\n", r);
return false;
}
app.DebugPrintf("UPnP: Port %d mapped successfully\n", port);
return true;
}
bool WinsockNetLayer::HostGame(int port)
{
if (!s_initialized && !Initialize()) return false;
@@ -206,6 +291,35 @@ bool WinsockNetLayer::HostGame(int port)
s_acceptThread = CreateThread(NULL, 0, AcceptThreadProc, NULL, 0, NULL);
app.DebugPrintf("Win64 LAN: Hosting on port %d\n", port);
char joinIp[64] = "";
s_upnpMapped = TryUPnP(port, s_externalIP);
if (s_upnpMapped && s_externalIP[0] != '\0')
{
// UPnP worked, use external IP for Discord invite
strncpy_s(joinIp, sizeof(joinIp), s_externalIP, _TRUNCATE);
printf("Win64 LAN: Using external IP for invite: %s\n", joinIp);
}
else
{
// Fall back to LAN IP
printf("Win64 LAN: UPnP failed, falling back to LAN IP\n");
char hostname[256];
if (gethostname(hostname, sizeof(hostname)) == 0)
{
struct addrinfo hints2 = {};
struct addrinfo* res = NULL;
hints2.ai_family = AF_INET;
hints2.ai_socktype = SOCK_STREAM;
if (getaddrinfo(hostname, NULL, &hints2, &res) == 0 && res != NULL)
{
inet_ntop(AF_INET, &((struct sockaddr_in*)res->ai_addr)->sin_addr, joinIp, sizeof(joinIp));
freeaddrinfo(res);
}
}
}
Discord_SetJoinSecret(joinIp, port);
return true;
}
@@ -479,6 +593,7 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
extern CPlatformNetworkManagerStub *g_pPlatformNetworkManager;
g_pPlatformNetworkManager->NotifyPlayerJoined(qnetPlayer);
ProfileManager.SetCurrentGameActivity(0, 4, false); // make sure it gets reset properly
DWORD *threadParam = new DWORD;
*threadParam = connIdx;

View File

@@ -60,6 +60,10 @@ struct Win64RemoteConnection
class WinsockNetLayer
{
public:
static bool s_upnpMapped;
static char s_externalIP[64];
static void CleanupUPnP(int port);
static bool Initialize();
static void Shutdown();

View File

@@ -38,6 +38,7 @@
#include "..\..\Minecraft.World\compression.h"
#include "..\..\Minecraft.World\OldChunkStorage.h"
#include "Network\WinsockNetLayer.h"
#include "x64\Extrax64Stubs.h"
#include "Xbox/resource.h"
@@ -1245,6 +1246,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
// Intro loop ?
while(app.IntroRunning())
{
ProfileManager.Tick();
// Tick XUI
app.RunFrame();
@@ -1275,6 +1277,8 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
if( msg.message == WM_QUIT ) break;
g_KBMInput.Tick();
#ifdef _DEBUG
for( int vk = 0; vk < 256; vk++ )
@@ -1356,6 +1360,8 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
g_NetworkManager.DoWork();
PIXEndNamedEvent();
TickDiscord();
// LeaderboardManager::Instance()->Tick();
// Render game graphics.
if(app.GetGameStarted())

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

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

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

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.

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

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

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

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 : miniupnp@free.fr
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

View File

@@ -0,0 +1 @@
2.3.3

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

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

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

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

View File

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

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

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,27 @@
/* $Id: miniupnpctypes.h,v 1.5 2025/03/02 01:15:44 nanard Exp $ */
/* Project: miniupnp
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org
* Author: Thomas Bernard
* Copyright (c) 2021-2025 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided within this distribution */
#ifndef MINIUPNPCTYPES_H_INCLUDED
#define MINIUPNPCTYPES_H_INCLUDED
/*! \file miniupnpctypes.h
* \brief type definitions
*
* Use unsigned long long when available :
* strtoull is C99
*
* \def UNSIGNED_INTEGER
* \brief `unsigned long long` or `unsigned int`
* \todo int can be 16 bits, so it should be `unsigned long`
*/
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define UNSIGNED_INTEGER unsigned long long
#else
#define UNSIGNED_INTEGER unsigned int
#endif
#endif

View File

@@ -0,0 +1,629 @@
/* $Id: miniwget.c,v 1.88 2025/05/25 21:56:49 nanard Exp $ */
/* Project : miniupnp
* Website : 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. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.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 socklen_t int
#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 */
#else /* #ifdef _WIN32 */
#include <unistd.h>
#include <sys/param.h>
#if defined(__amigaos__) && !defined(__amigaos4__)
#define socklen_t int
#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
#include <sys/select.h>
#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#define closesocket close
#include <strings.h>
#endif /* #else _WIN32 */
#ifdef __GNU__
#define MAXHOSTNAMELEN 64
#endif /* __GNU__ */
#ifndef MIN
#define MIN(x,y) (((x)<(y))?(x):(y))
#endif /* MIN */
#include "miniupnpcstrings.h"
#include "miniwget.h"
#include "connecthostport.h"
#include "receivedata.h"
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif
/*
* Read a HTTP response from a socket.
* Process Content-Length and Transfer-encoding headers.
* return a pointer to the content buffer, which length is saved
* to the length parameter.
*/
void *
getHTTPResponse(SOCKET s, int * size, int * status_code)
{
char buf[2048];
int n;
int endofheaders = 0;
int chunked = 0;
int content_length = -1;
unsigned int chunksize = 0;
unsigned int bytestocopy = 0;
/* buffers : */
char * header_buf;
unsigned int header_buf_len = 2048;
unsigned int header_buf_used = 0;
char * content_buf;
unsigned int content_buf_len = 2048;
unsigned int content_buf_used = 0;
char chunksize_buf[32];
unsigned int chunksize_buf_index;
#ifdef DEBUG
char * reason_phrase = NULL;
int reason_phrase_len = 0;
#endif
if(status_code) *status_code = -1;
header_buf = malloc(header_buf_len);
if(header_buf == NULL)
{
#ifdef DEBUG
fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
#endif /* DEBUG */
*size = -1;
return NULL;
}
content_buf = malloc(content_buf_len);
if(content_buf == NULL)
{
free(header_buf);
#ifdef DEBUG
fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
#endif /* DEBUG */
*size = -1;
return NULL;
}
chunksize_buf[0] = '\0';
chunksize_buf_index = 0;
while((n = receivedata(s, buf, sizeof(buf), 5000, NULL)) > 0)
{
if(endofheaders == 0)
{
int i;
int linestart=0;
int colon=0;
int valuestart=0;
if(header_buf_used + n > header_buf_len) {
char * tmp = realloc(header_buf, header_buf_used + n);
if(tmp == NULL) {
/* memory allocation error */
free(header_buf);
free(content_buf);
*size = -1;
return NULL;
}
header_buf = tmp;
header_buf_len = header_buf_used + n;
}
memcpy(header_buf + header_buf_used, buf, n);
header_buf_used += n;
/* search for CR LF CR LF (end of headers)
* recognize also LF LF */
i = 0;
while(i < ((int)header_buf_used-1) && (endofheaders == 0)) {
if(header_buf[i] == '\r') {
i++;
if(header_buf[i] == '\n') {
i++;
if(i < (int)header_buf_used && header_buf[i] == '\r') {
i++;
if(i < (int)header_buf_used && header_buf[i] == '\n') {
endofheaders = i+1;
}
}
}
} else if(header_buf[i] == '\n') {
i++;
if(header_buf[i] == '\n') {
endofheaders = i+1;
}
}
i++;
}
if(endofheaders == 0)
continue;
/* parse header lines */
for(i = 0; i < endofheaders - 1; i++) {
if(linestart > 0 && colon <= linestart && header_buf[i]==':')
{
colon = i;
while(i < (endofheaders-1)
&& (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
i++;
valuestart = i + 1;
}
/* detecting end of line */
else if(header_buf[i]=='\r' || header_buf[i]=='\n')
{
if(linestart == 0 && status_code)
{
/* Status line
* HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
int sp;
for(sp = 0; sp < i - 1; sp++)
if(header_buf[sp] == ' ')
{
if(*status_code < 0)
{
if (header_buf[sp+1] >= '1' && header_buf[sp+1] <= '9')
*status_code = atoi(header_buf + sp + 1);
}
else
{
#ifdef DEBUG
reason_phrase = header_buf + sp + 1;
reason_phrase_len = i - sp - 1;
#endif
break;
}
}
#ifdef DEBUG
printf("HTTP status code = %d, Reason phrase = %.*s\n",
*status_code, reason_phrase_len, reason_phrase);
#endif
}
else if(colon > linestart && valuestart > colon)
{
#ifdef DEBUG
printf("header='%.*s', value='%.*s'\n",
colon-linestart, header_buf+linestart,
i-valuestart, header_buf+valuestart);
#endif
if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
{
content_length = atoi(header_buf+valuestart);
#ifdef DEBUG
printf("Content-Length: %d\n", content_length);
#endif
}
else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
&& 0==strncasecmp(header_buf+valuestart, "chunked", 7))
{
#ifdef DEBUG
printf("chunked transfer-encoding!\n");
#endif
chunked = 1;
}
}
while((i < (int)header_buf_used) && (header_buf[i]=='\r' || header_buf[i] == '\n'))
i++;
linestart = i;
colon = linestart;
valuestart = 0;
}
}
/* copy the remaining of the received data back to buf */
n = header_buf_used - endofheaders;
memcpy(buf, header_buf + endofheaders, n);
/* if(headers) */
}
/* if we get there, endofheaders != 0.
* In the other case, there was a continue above */
/* content */
if(chunked)
{
int i = 0;
while(i < n)
{
if(chunksize == 0)
{
/* reading chunk size */
if(chunksize_buf_index == 0) {
/* skipping any leading CR LF */
if(buf[i] == '\r') i++;
if(i<n && buf[i] == '\n') i++;
}
while(i<n && isxdigit(buf[i])
&& chunksize_buf_index < (sizeof(chunksize_buf)-1))
{
chunksize_buf[chunksize_buf_index++] = buf[i];
chunksize_buf[chunksize_buf_index] = '\0';
i++;
}
while(i<n && buf[i] != '\r' && buf[i] != '\n')
i++; /* discarding chunk-extension */
if(i<n && buf[i] == '\r') i++;
if(i<n && buf[i] == '\n') {
unsigned int j;
for(j = 0; j < chunksize_buf_index; j++) {
if(chunksize_buf[j] >= '0'
&& chunksize_buf[j] <= '9')
chunksize = (chunksize << 4) + (chunksize_buf[j] - '0');
else
chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10);
}
chunksize_buf[0] = '\0';
chunksize_buf_index = 0;
i++;
} else {
/* not finished to get chunksize */
continue;
}
#ifdef DEBUG
printf("chunksize = %u (%x)\n", chunksize, chunksize);
#endif
if(chunksize == 0)
{
#ifdef DEBUG
printf("end of HTTP content - %d %d\n", i, n);
/*printf("'%.*s'\n", n-i, buf+i);*/
#endif
goto end_of_stream;
}
}
/* it is guaranteed that (n >= i) */
bytestocopy = (chunksize < (unsigned int)(n - i))?chunksize:(unsigned int)(n - i);
if((content_buf_used + bytestocopy) > content_buf_len)
{
char * tmp;
if((content_length >= 0) && ((unsigned int)content_length >= (content_buf_used + bytestocopy))) {
content_buf_len = content_length;
} else {
content_buf_len = content_buf_used + bytestocopy;
}
tmp = realloc(content_buf, content_buf_len);
if(tmp == NULL) {
/* memory allocation error */
free(content_buf);
free(header_buf);
*size = -1;
return NULL;
}
content_buf = tmp;
}
memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
content_buf_used += bytestocopy;
i += bytestocopy;
chunksize -= bytestocopy;
}
}
else
{
/* not chunked */
if(content_length > 0
&& (content_buf_used + n) > (unsigned int)content_length) {
/* skipping additional bytes */
n = content_length - content_buf_used;
}
if(content_buf_used + n > content_buf_len)
{
char * tmp;
if(content_length >= 0
&& (unsigned int)content_length >= (content_buf_used + n)) {
content_buf_len = content_length;
} else {
content_buf_len = content_buf_used + n;
}
tmp = realloc(content_buf, content_buf_len);
if(tmp == NULL) {
/* memory allocation error */
free(content_buf);
free(header_buf);
*size = -1;
return NULL;
}
content_buf = tmp;
}
memcpy(content_buf + content_buf_used, buf, n);
content_buf_used += n;
}
/* use the Content-Length header value if available */
if(content_length > 0 && content_buf_used >= (unsigned int)content_length)
{
#ifdef DEBUG
printf("End of HTTP content\n");
#endif
break;
}
}
end_of_stream:
free(header_buf);
*size = content_buf_used;
if(content_buf_used == 0)
{
free(content_buf);
content_buf = NULL;
}
return content_buf;
}
/* miniwget3() :
* do all the work.
* Return NULL if something failed. */
static void *
miniwget3(const char * host,
unsigned short port, const char * path,
int * size, char * addr_str, int addr_str_len,
const char * httpversion, unsigned int scope_id,
int * status_code)
{
char buf[2048];
SOCKET s;
int n;
int len;
int sent;
void * content;
*size = 0;
s = connecthostport(host, port, scope_id);
if(ISINVALID(s))
return NULL;
/* get address for caller ! */
if(addr_str)
{
struct sockaddr_storage saddr;
socklen_t saddrlen;
saddrlen = sizeof(saddr);
if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0)
{
perror("getsockname");
}
else
{
#if defined(__amigaos__) && !defined(__amigaos4__)
/* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
* But his function make a string with the port : nn.nn.nn.nn:port */
/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
NULL, addr_str, (DWORD *)&addr_str_len))
{
printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
}*/
/* the following code is only compatible with ip v4 addresses */
strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
#else
#if 0
if(saddr.sa_family == AF_INET6) {
inet_ntop(AF_INET6,
&(((struct sockaddr_in6 *)&saddr)->sin6_addr),
addr_str, addr_str_len);
} else {
inet_ntop(AF_INET,
&(((struct sockaddr_in *)&saddr)->sin_addr),
addr_str, addr_str_len);
}
#endif
/* getnameinfo return ip v6 address with the scope identifier
* such as : 2a01:e35:8b2b:7330::%4281128194 */
n = getnameinfo((const struct sockaddr *)&saddr, saddrlen,
addr_str, addr_str_len,
NULL, 0,
NI_NUMERICHOST | NI_NUMERICSERV);
if(n != 0) {
#ifdef _WIN32
fprintf(stderr, "getnameinfo() failed : %d\n", n);
#else
fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
#endif
}
#endif
}
#ifdef DEBUG
printf("address miniwget : %s\n", addr_str);
#endif
}
len = snprintf(buf, sizeof(buf),
"GET %s HTTP/%s\r\n"
"Host: %s:%d\r\n"
"Connection: close\r\n"
"User-Agent: " OS_STRING " " UPNP_VERSION_STRING " MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
"\r\n",
path, httpversion, host, port);
if ((unsigned int)len >= sizeof(buf))
{
closesocket(s);
return NULL;
}
sent = 0;
/* sending the HTTP request */
while(sent < len)
{
n = send(s, buf+sent, len-sent, 0);
if(n < 0)
{
perror("send");
closesocket(s);
return NULL;
}
else
{
sent += n;
}
}
content = getHTTPResponse(s, size, status_code);
closesocket(s);
return content;
}
/* parseURL()
* arguments :
* url : source string not modified
* hostname : hostname destination string (size of MAXHOSTNAMELEN+1)
* port : port (destination)
* path : pointer to the path part of the URL
*
* Return values :
* 0 - Failure
* 1 - Success */
int
parseURL(const char * url,
char * hostname, unsigned short * port,
char * * path, unsigned int * scope_id)
{
char * p1, *p2, *p3;
if(!url)
return 0;
p1 = strstr(url, "://");
if(!p1)
return 0;
p1 += 3;
if( (url[0]!='h') || (url[1]!='t')
||(url[2]!='t') || (url[3]!='p'))
return 0;
memset(hostname, 0, MAXHOSTNAMELEN + 1);
if(*p1 == '[')
{
/* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
char * scope;
scope = strchr(p1, '%');
p2 = strchr(p1, ']');
if(p2 && scope && scope < p2 && scope_id) {
/* parse scope */
#ifdef IF_NAMESIZE
char tmp[IF_NAMESIZE];
int l;
scope++;
/* "%25" is just '%' in URL encoding */
if(scope[0] == '2' && scope[1] == '5')
scope += 2; /* skip "25" */
l = p2 - scope;
if(l >= IF_NAMESIZE)
l = IF_NAMESIZE - 1;
memcpy(tmp, scope, l);
tmp[l] = '\0';
*scope_id = if_nametoindex(tmp);
if(*scope_id == 0) {
*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
}
#else
/* under windows, scope is numerical */
char tmp[8];
size_t l;
scope++;
/* "%25" is just '%' in URL encoding */
if(scope[0] == '2' && scope[1] == '5')
scope += 2; /* skip "25" */
l = p2 - scope;
if(l >= sizeof(tmp))
l = sizeof(tmp) - 1;
memcpy(tmp, scope, l);
tmp[l] = '\0';
*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
#endif
}
p3 = strchr(p1, '/');
if(p2 && p3)
{
p2++;
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
if(*p2 == ':')
{
*port = 0;
p2++;
while( (*p2 >= '0') && (*p2 <= '9'))
{
*port *= 10;
*port += (unsigned short)(*p2 - '0');
p2++;
}
}
else
{
*port = 80;
}
*path = p3;
return 1;
}
}
p2 = strchr(p1, ':');
p3 = strchr(p1, '/');
if(!p3)
return 0;
if(!p2 || (p2>p3))
{
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
*port = 80;
}
else
{
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
*port = 0;
p2++;
while( (*p2 >= '0') && (*p2 <= '9'))
{
*port *= 10;
*port += (unsigned short)(*p2 - '0');
p2++;
}
}
*path = p3;
return 1;
}
void *
miniwget(const char * url, int * size,
unsigned int scope_id, int * status_code)
{
unsigned short port;
char * path;
/* protocol://host:port/chemin */
char hostname[MAXHOSTNAMELEN+1];
*size = 0;
if(!parseURL(url, hostname, &port, &path, &scope_id))
return NULL;
#ifdef DEBUG
printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
hostname, port, path, scope_id);
#endif
return miniwget3(hostname, port, path, size, 0, 0, "1.1", scope_id, status_code);
}
void *
miniwget_getaddr(const char * url, int * size,
char * addr, int addrlen, unsigned int scope_id,
int * status_code)
{
unsigned short port;
char * path;
/* protocol://host:port/path */
char hostname[MAXHOSTNAMELEN+1];
*size = 0;
if(addr)
addr[0] = '\0';
if(!parseURL(url, hostname, &port, &path, &scope_id))
return NULL;
#ifdef DEBUG
printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
hostname, port, path, scope_id);
#endif
return miniwget3(hostname, port, path, size, addr, addrlen, "1.1", scope_id, status_code);
}

View File

@@ -0,0 +1,54 @@
/* $Id: miniwget.h,v 1.15 2025/03/02 01:14:38 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Copyright (c) 2005-2025 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
#ifndef MINIWGET_H_INCLUDED
#define MINIWGET_H_INCLUDED
/*! \file miniwget.h
* \brief Lightweight HTTP client API
*/
#include "miniupnpc_declspec.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \brief perform HTTP GET on an URL
*
* \param[in] url HTTP URL to GET
* \param[out] size length of the returned buffer. -1 in case of memory
* allocation error
* \param[in] scope_id interface id for IPv6 to use if not specified in the URL
* \param[out] status_code HTTP response status code (200, 404, etc.)
* \return the body of the HTTP response
*/
MINIUPNP_LIBSPEC void * miniwget(const char * url, int * size,
unsigned int scope_id, int * status_code);
/*! \brief perform HTTP GET on an URL
*
* Also get the local address used to reach the HTTP server
*
* \param[in] url HTTP URL to GET
* \param[out] size length of the returned buffer. -1 in case of memory
* allocation error
* \param[out] addr local address used to connect to the server
* \param[in] addrlen size of the addr buffer
* \param[in] scope_id interface id for IPv6 to use if not specified in the URL
* \param[out] status_code HTTP response status code (200, 404, etc.)
* \return the body of the HTTP response
*/
MINIUPNP_LIBSPEC void * miniwget_getaddr(const char * url, int * size,
char * addr, int addrlen,
unsigned int scope_id, int * status_code);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,54 @@
/* $Id: miniwget_private.h,v 1.2 2025/03/02 01:14:38 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2018-2025 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
#ifndef MINIWGET_INTERNAL_H_INCLUDED
#define MINIWGET_INTERNAL_H_INCLUDED
/*! \file miniwget_private.h
* \brief Lightweight HTTP client private API
*/
#include "miniupnpc_socketdef.h"
/*! \brief Read a HTTP response from a socket
*
* Processed HTTP headers :
* - `Content-Length`
* - `Transfer-encoding`
* return a pointer to the content buffer, which length is saved
* to the length parameter.
* \param[in] s socket
* \param[out] size returned content buffer size
* \param[out] status_code HTTP Status code
* \return malloc'ed content buffer
*/
void * getHTTPResponse(SOCKET s, int * size, int * status_code);
/*! \brief parse a HTTP URL
*
* URL formats supported :
* - `http://192.168.1.1/path/xxx`
* - `http://192.168.1.1:8080/path/xxx`
* - `http://[2a00:1234:5678:90ab::123]/path/xxx`
* - `http://[2a00:1234:5678:90ab::123]:8080/path/xxx`
* - `http://[fe80::1234:5678:90ab%%eth0]/path/xxx`
* - `http://[fe80::1234:5678:90ab%%eth0]:8080/path/xxx`
*
* `%` may be encoded as `%25`
*
* \param[in] url URL to parse
* \param[out] hostname hostname part of the URL (size of MAXHOSTNAMELEN+1)
* \param[out] port set to the port specified in the URL or 80
* \param[out] path set to the begining of the path part of the URL
* \param[out] scope_id set to the interface id if specified in the
* link-local IPv6 address
* \return 0 for failure, 1 for success
*/
int parseURL(const char * url,
char * hostname, unsigned short * port, char * * path,
unsigned int * scope_id);
#endif

View File

@@ -0,0 +1,231 @@
/* $Id: minixml.c,v 1.12 2017/12/12 11:17:40 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* minixml.c : the minimum size a xml parser can be ! */
/* Project : miniupnp
* webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
Copyright (c) 2005-2017, 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:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* 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.
* The name of the author may not 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 OWNER 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.
*/
#include <string.h>
#include "minixml.h"
/* parseatt : used to parse the argument list
* return 0 (false) in case of success and -1 (true) if the end
* of the xmlbuffer is reached. */
static int parseatt(struct xmlparser * p)
{
const char * attname;
int attnamelen;
const char * attvalue;
int attvaluelen;
while(p->xml < p->xmlend)
{
if(*p->xml=='/' || *p->xml=='>')
return 0;
if( !IS_WHITE_SPACE(*p->xml) )
{
char sep;
attname = p->xml;
attnamelen = 0;
while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) )
{
attnamelen++; p->xml++;
if(p->xml >= p->xmlend)
return -1;
}
while(*(p->xml++) != '=')
{
if(p->xml >= p->xmlend)
return -1;
}
while(IS_WHITE_SPACE(*p->xml))
{
p->xml++;
if(p->xml >= p->xmlend)
return -1;
}
sep = *p->xml;
if(sep=='\'' || sep=='\"')
{
p->xml++;
if(p->xml >= p->xmlend)
return -1;
attvalue = p->xml;
attvaluelen = 0;
while(*p->xml != sep)
{
attvaluelen++; p->xml++;
if(p->xml >= p->xmlend)
return -1;
}
}
else
{
attvalue = p->xml;
attvaluelen = 0;
while( !IS_WHITE_SPACE(*p->xml)
&& *p->xml != '>' && *p->xml != '/')
{
attvaluelen++; p->xml++;
if(p->xml >= p->xmlend)
return -1;
}
}
/*printf("%.*s='%.*s'\n",
attnamelen, attname, attvaluelen, attvalue);*/
if(p->attfunc)
p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen);
}
p->xml++;
}
return -1;
}
/* parseelt parse the xml stream and
* call the callback functions when needed... */
static void parseelt(struct xmlparser * p)
{
int i;
const char * elementname;
while(p->xml < (p->xmlend - 1))
{
if((p->xml + 4) <= p->xmlend && (0 == memcmp(p->xml, "<!--", 4)))
{
p->xml += 3;
/* ignore comments */
do
{
p->xml++;
if ((p->xml + 3) >= p->xmlend)
return;
}
while(memcmp(p->xml, "-->", 3) != 0);
p->xml += 3;
}
else if((p->xml)[0]=='<' && (p->xml)[1]!='?')
{
i = 0; elementname = ++p->xml;
while( !IS_WHITE_SPACE(*p->xml)
&& (*p->xml!='>') && (*p->xml!='/')
)
{
i++; p->xml++;
if (p->xml >= p->xmlend)
return;
/* to ignore namespace : */
if(*p->xml==':')
{
i = 0;
elementname = ++p->xml;
}
}
if(i>0)
{
if(p->starteltfunc)
p->starteltfunc(p->data, elementname, i);
if(parseatt(p))
return;
if(*p->xml!='/')
{
const char * data;
i = 0; data = ++p->xml;
if (p->xml >= p->xmlend)
return;
while( IS_WHITE_SPACE(*p->xml) )
{
i++; p->xml++;
if (p->xml >= p->xmlend)
return;
}
/* CDATA are at least 9 + 3 characters long : <![CDATA[ ]]> */
if((p->xmlend >= (p->xml + (9 + 3))) && (memcmp(p->xml, "<![CDATA[", 9) == 0))
{
/* CDATA handling */
p->xml += 9;
data = p->xml;
i = 0;
while(memcmp(p->xml, "]]>", 3) != 0)
{
i++; p->xml++;
if ((p->xml + 3) >= p->xmlend)
return;
}
if(i>0 && p->datafunc)
p->datafunc(p->data, data, i);
while(*p->xml!='<')
{
p->xml++;
if (p->xml >= p->xmlend)
return;
}
}
else
{
while(*p->xml!='<')
{
i++; p->xml++;
if ((p->xml + 1) >= p->xmlend)
return;
}
if(i>0 && p->datafunc && *(p->xml + 1) == '/')
p->datafunc(p->data, data, i);
}
}
}
else if(*p->xml == '/')
{
i = 0; elementname = ++p->xml;
if (p->xml >= p->xmlend)
return;
while((*p->xml != '>'))
{
i++; p->xml++;
if (p->xml >= p->xmlend)
return;
}
if(p->endeltfunc)
p->endeltfunc(p->data, elementname, i);
p->xml++;
}
}
else
{
p->xml++;
}
}
}
/* the parser must be initialized before calling this function */
void parsexml(struct xmlparser * parser)
{
parser->xml = parser->xmlstart;
parser->xmlend = parser->xmlstart + parser->xmlsize;
parseelt(parser);
}

View File

@@ -0,0 +1,37 @@
/* $Id: minixml.h,v 1.8 2019/02/10 12:29:25 nanard Exp $ */
/* minimal xml parser
*
* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Author : Thomas Bernard
* Copyright (c) 2005 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
#ifndef MINIXML_H_INCLUDED
#define MINIXML_H_INCLUDED
#define IS_WHITE_SPACE(c) ((c)==' ' || (c)=='\t' || (c)=='\r' || (c)=='\n')
/* if a callback function pointer is set to NULL,
* the function is not called */
struct xmlparser {
const char *xmlstart;
const char *xmlend;
const char *xml; /* pointer to current character */
int xmlsize;
void * data;
void (*starteltfunc) (void *, const char *, int);
void (*endeltfunc) (void *, const char *, int);
void (*datafunc) (void *, const char *, int);
void (*attfunc) (void *, const char *, int, const char *, int);
};
/* parsexml()
* the xmlparser structure must be initialized before the call
* the following structure members have to be initialized :
* xmlstart, xmlsize, data, *func
* xml is for internal usage, xmlend is computed automatically */
void parsexml(struct xmlparser *);
#endif

View File

@@ -0,0 +1,163 @@
/* $Id: minixmlvalid.c,v 1.7 2015/07/15 12:41:15 nanard Exp $ */
/* MiniUPnP Project
* http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
* minixmlvalid.c :
* validation program for the minixml parser
*
* (c) 2006-2011 Thomas Bernard */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "minixml.h"
/* xml event structure */
struct event {
enum { ELTSTART, ELTEND, ATT, CHARDATA } type;
const char * data;
int len;
};
struct eventlist {
int n;
struct event * events;
};
/* compare 2 xml event lists
* return 0 if the two lists are equals */
int evtlistcmp(struct eventlist * a, struct eventlist * b)
{
int i;
struct event * ae, * be;
if(a->n != b->n)
{
printf("event number not matching : %d != %d\n", a->n, b->n);
/*return 1;*/
}
for(i=0; i<a->n; i++)
{
ae = a->events + i;
be = b->events + i;
if( (ae->type != be->type)
||(ae->len != be->len)
||memcmp(ae->data, be->data, ae->len))
{
printf("Found a difference : %d '%.*s' != %d '%.*s'\n",
ae->type, ae->len, ae->data,
be->type, be->len, be->data);
return 1;
}
}
return 0;
}
/* Test data */
static const char xmldata[] =
"<xmlroot>\n"
" <elt1 att1=\"attvalue1\" att2=\"attvalue2\">"
"character data"
"</elt1> \n \t"
"<elt1b/>"
"<elt1>\n<![CDATA[ <html>stuff !\n ]]> \n</elt1>\n"
"<elt2a> \t<elt2b>chardata1</elt2b><elt2b> chardata2 </elt2b></elt2a>"
"</xmlroot>";
static const struct event evtref[] =
{
{ELTSTART, "xmlroot", 7},
{ELTSTART, "elt1", 4},
/* attributes */
{CHARDATA, "character data", 14},
{ELTEND, "elt1", 4},
{ELTSTART, "elt1b", 5},
{ELTSTART, "elt1", 4},
{CHARDATA, " <html>stuff !\n ", 16},
{ELTEND, "elt1", 4},
{ELTSTART, "elt2a", 5},
{ELTSTART, "elt2b", 5},
{CHARDATA, "chardata1", 9},
{ELTEND, "elt2b", 5},
{ELTSTART, "elt2b", 5},
{CHARDATA, " chardata2 ", 11},
{ELTEND, "elt2b", 5},
{ELTEND, "elt2a", 5},
{ELTEND, "xmlroot", 7}
};
void startelt(void * data, const char * p, int l)
{
struct eventlist * evtlist = data;
struct event * evt;
evt = evtlist->events + evtlist->n;
/*printf("startelt : %.*s\n", l, p);*/
evt->type = ELTSTART;
evt->data = p;
evt->len = l;
evtlist->n++;
}
void endelt(void * data, const char * p, int l)
{
struct eventlist * evtlist = data;
struct event * evt;
evt = evtlist->events + evtlist->n;
/*printf("endelt : %.*s\n", l, p);*/
evt->type = ELTEND;
evt->data = p;
evt->len = l;
evtlist->n++;
}
void chardata(void * data, const char * p, int l)
{
struct eventlist * evtlist = data;
struct event * evt;
evt = evtlist->events + evtlist->n;
/*printf("chardata : '%.*s'\n", l, p);*/
evt->type = CHARDATA;
evt->data = p;
evt->len = l;
evtlist->n++;
}
int testxmlparser(const char * xml, int size)
{
int r;
struct eventlist evtlist;
struct eventlist evtlistref;
struct xmlparser parser;
evtlist.n = 0;
evtlist.events = malloc(sizeof(struct event)*100);
if(evtlist.events == NULL)
{
fprintf(stderr, "Memory allocation error.\n");
return -1;
}
memset(&parser, 0, sizeof(parser));
parser.xmlstart = xml;
parser.xmlsize = size;
parser.data = &evtlist;
parser.starteltfunc = startelt;
parser.endeltfunc = endelt;
parser.datafunc = chardata;
parsexml(&parser);
printf("%d events\n", evtlist.n);
/* compare */
evtlistref.n = sizeof(evtref)/sizeof(struct event);
evtlistref.events = (struct event *)evtref;
r = evtlistcmp(&evtlistref, &evtlist);
free(evtlist.events);
return r;
}
int main(int argc, char * * argv)
{
int r;
(void)argc; (void)argv;
r = testxmlparser(xmldata, sizeof(xmldata)-1);
if(r)
printf("minixml validation test failed\n");
return r;
}

View File

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

View File

@@ -0,0 +1,29 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual C++ Express 2008
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc", "miniupnpc.vcproj", "{D28CE435-CB33-4BAE-8A52-C6EF915956F5}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "upnpc-static", "upnpc-static.vcproj", "{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}"
ProjectSection(ProjectDependencies) = postProject
{D28CE435-CB33-4BAE-8A52-C6EF915956F5} = {D28CE435-CB33-4BAE-8A52-C6EF915956F5}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.ActiveCfg = Debug|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.Build.0 = Debug|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.ActiveCfg = Release|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.Build.0 = Release|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.ActiveCfg = Debug|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.Build.0 = Debug|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.ActiveCfg = Release|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,283 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="miniupnpc"
ProjectGUID="{D28CE435-CB33-4BAE-8A52-C6EF915956F5}"
RootNamespace="miniupnpc"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;DEBUG"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
PreprocessorDefinitions="_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Fichiers sources"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\connecthostport.c"
>
</File>
<File
RelativePath="..\igd_desc_parse.c"
>
</File>
<File
RelativePath="..\minisoap.c"
>
</File>
<File
RelativePath="..\minissdpc.c"
>
</File>
<File
RelativePath="..\miniupnpc.c"
>
</File>
<File
RelativePath="..\miniwget.c"
>
</File>
<File
RelativePath="..\minixml.c"
>
</File>
<File
RelativePath="..\portlistingparse.c"
>
</File>
<File
RelativePath="..\receivedata.c"
>
</File>
<File
RelativePath="..\upnpcommands.c"
>
</File>
<File
RelativePath="..\upnpdev.c"
>
</File>
<File
RelativePath="..\upnperrors.c"
>
</File>
<File
RelativePath="..\upnpreplyparse.c"
>
</File>
</Filter>
<Filter
Name="Fichiers d&apos;en-t<>te"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\connecthostport.h"
>
</File>
<File
RelativePath="..\declspec.h"
>
</File>
<File
RelativePath="..\igd_desc_parse.h"
>
</File>
<File
RelativePath="..\minisoap.h"
>
</File>
<File
RelativePath="..\minissdpc.h"
>
</File>
<File
RelativePath="..\miniupnpc.h"
>
</File>
<File
RelativePath="..\miniupnpcstrings.h"
>
</File>
<File
RelativePath="..\miniupnpctypes.h"
>
</File>
<File
RelativePath="..\miniwget.h"
>
</File>
<File
RelativePath="..\minixml.h"
>
</File>
<File
RelativePath="..\portlistingparse.h"
>
</File>
<File
RelativePath="..\receivedata.h"
>
</File>
<File
RelativePath="..\upnpcommands.h"
>
</File>
<File
RelativePath="..\upnpdev.h"
>
</File>
<File
RelativePath="..\upnperrors.h"
>
</File>
<File
RelativePath="..\upnpreplyparse.h"
>
</File>
</Filter>
<Filter
Name="Fichiers de ressources"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,215 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug Dll|Win32">
<Configuration>Debug Dll</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release Dll|Win32">
<Configuration>Release Dll</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{D28CE435-CB33-4BAE-8A52-C6EF915956F5}</ProjectGuid>
<RootNamespace>miniupnpc</RootNamespace>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>14.0.25123.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<PreBuildEvent>
<Command>genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
<ResourceCompile>
<PreprocessorDefinitions>INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_EXPORTS;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<PreBuildEvent>
<Command>genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
<Link>
<AdditionalDependencies>ws2_32.lib;IPHlpApi.Lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<ResourceCompile>
<PreprocessorDefinitions>INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<PreBuildEvent>
<Command>genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
<ResourceCompile>
<PreprocessorDefinitions>INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<PreBuildEvent>
<Command>genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
<Link>
<AdditionalDependencies>ws2_32.lib;IPHlpApi.Lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<ResourceCompile>
<PreprocessorDefinitions>INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\addr_is_reserved.c" />
<ClCompile Include="..\src\connecthostport.c" />
<ClCompile Include="..\src\igd_desc_parse.c" />
<ClCompile Include="..\src\minisoap.c" />
<ClCompile Include="..\src\minissdpc.c" />
<ClCompile Include="..\src\miniupnpc.c" />
<ClCompile Include="..\src\miniwget.c" />
<ClCompile Include="..\src\minixml.c" />
<ClCompile Include="..\src\portlistingparse.c" />
<ClCompile Include="..\src\receivedata.c" />
<ClCompile Include="..\src\upnpcommands.c" />
<ClCompile Include="..\src\upnpdev.c" />
<ClCompile Include="..\src\upnperrors.c" />
<ClCompile Include="..\src\upnpreplyparse.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\addr_is_reserved.h" />
<ClInclude Include="..\src\connecthostport.h" />
<ClInclude Include="..\include\igd_desc_parse.h" />
<ClInclude Include="..\src\minisoap.h" />
<ClInclude Include="..\src\minissdpc.h" />
<ClInclude Include="..\include\miniupnpc.h" />
<ClInclude Include="..\miniupnpcstrings.h" />
<ClInclude Include="..\include\miniupnpctypes.h" />
<ClInclude Include="..\include\miniupnpc_declspec.h" />
<ClInclude Include="..\include\miniwget.h" />
<ClInclude Include="..\src\miniwget_private.h" />
<ClInclude Include="..\src\minixml.h" />
<ClInclude Include="..\include\portlistingparse.h" />
<ClInclude Include="..\src\receivedata.h" />
<ClInclude Include="..\include\upnpcommands.h" />
<ClInclude Include="..\include\upnpdev.h" />
<ClInclude Include="..\include\upnperrors.h" />
<ClInclude Include="..\include\upnpreplyparse.h" />
<ClInclude Include="..\src\win32_snprintf.h" />
<ClInclude Include="..\rc_version.h" />
</ItemGroup>
<ItemGroup>
<None Include="genminiupnpcstrings.vbs" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="../miniupnpc.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Fichiers sources">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Fichiers d%27en-tête">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Fichiers de ressources">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
</Filter>
<Filter Include="scripts">
<UniqueIdentifier>{508da401-2f8e-4fdb-9a43-95baa95a91e7}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\addr_is_reserved.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\src\connecthostport.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\src\igd_desc_parse.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\src\minisoap.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\src\minissdpc.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\src\miniupnpc.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\src\miniwget.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\src\minixml.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\src\portlistingparse.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\src\receivedata.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\src\upnpcommands.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\src\upnpdev.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\src\upnperrors.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\src\upnpreplyparse.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\rc_version.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\src\addr_is_reserved.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\src\connecthostport.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\include\igd_desc_parse.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\src\minisoap.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\src\minissdpc.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\include\miniupnpc.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\include\miniupnpcstrings.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\include\miniupnpctypes.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\include\miniupnpc_declspec.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\include\miniwget.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\src\minixml.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\include\portlistingparse.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\include\receivedata.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\include\upnpcommands.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\include\upnpdev.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\include\upnperrors.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\include\upnpreplyparse.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\src\win32_snprintf.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="genminiupnpcstrings.vbs">
<Filter>scripts</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="../miniupnpc.rc">
<Filter>Fichiers de ressources</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual C++ Express 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc_vs2010", "miniupnpc_vs2010.vcxproj", "{D28CE435-CB33-4BAE-8A52-C6EF915956F5}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "upnpc-static_vs2010", "upnpc-static_vs2010.vcxproj", "{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug Dll|Win32 = Debug Dll|Win32
Debug|Win32 = Debug|Win32
Release Dll|Win32 = Release Dll|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug Dll|Win32.ActiveCfg = Debug Dll|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug Dll|Win32.Build.0 = Debug Dll|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.ActiveCfg = Debug|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.Build.0 = Debug|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release Dll|Win32.ActiveCfg = Release Dll|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release Dll|Win32.Build.0 = Release Dll|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.ActiveCfg = Release|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.Build.0 = Release|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug Dll|Win32.ActiveCfg = Debug Dll|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug Dll|Win32.Build.0 = Debug Dll|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.ActiveCfg = Debug|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.Build.0 = Debug|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release Dll|Win32.ActiveCfg = Release Dll|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release Dll|Win32.Build.0 = Release Dll|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.ActiveCfg = Release|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,209 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug Dll|Win32">
<Configuration>Debug Dll</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release Dll|Win32">
<Configuration>Release Dll</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{D28CE435-CB33-4BAE-8A52-C6EF915956F5}</ProjectGuid>
<RootNamespace>miniupnpc</RootNamespace>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">$(Configuration)\</IntDir>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">$(Configuration)\</IntDir>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">miniupnpc</TargetName>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">miniupnpc</TargetName>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">miniupnpc</TargetName>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">miniupnpc</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<PreBuildEvent>
<Command>genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
<ResourceCompile>
<PreprocessorDefinitions>INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_EXPORTS;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<PreBuildEvent>
<Command>genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
<Link>
<AdditionalDependencies>ws2_32.lib;IPHlpApi.Lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<ResourceCompile>
<PreprocessorDefinitions>INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<PreBuildEvent>
<Command>genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
<ResourceCompile>
<PreprocessorDefinitions>INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<PreBuildEvent>
<Command>genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
<Link>
<AdditionalDependencies>ws2_32.lib;IPHlpApi.Lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<ResourceCompile>
<PreprocessorDefinitions>INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\addr_is_reserved.c" />
<ClCompile Include="..\src\connecthostport.c" />
<ClCompile Include="..\src\igd_desc_parse.c" />
<ClCompile Include="..\src\minisoap.c" />
<ClCompile Include="..\src\minissdpc.c" />
<ClCompile Include="..\src\miniupnpc.c" />
<ClCompile Include="..\src\miniwget.c" />
<ClCompile Include="..\src\minixml.c" />
<ClCompile Include="..\src\portlistingparse.c" />
<ClCompile Include="..\src\receivedata.c" />
<ClCompile Include="..\src\upnpcommands.c" />
<ClCompile Include="..\src\upnpdev.c" />
<ClCompile Include="..\src\upnperrors.c" />
<ClCompile Include="..\src\upnpreplyparse.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\addr_is_reserved.h" />
<ClInclude Include="..\src\connecthostport.h" />
<ClInclude Include="..\include\igd_desc_parse.h" />
<ClInclude Include="..\src\minisoap.h" />
<ClInclude Include="..\src\minissdpc.h" />
<ClInclude Include="..\include\miniupnpc.h" />
<ClInclude Include="..\miniupnpcstrings.h" />
<ClInclude Include="..\include\miniupnpctypes.h" />
<ClInclude Include="..\include\miniupnpc_declspec.h" />
<ClInclude Include="..\include\miniwget.h" />
<ClInclude Include="..\src\miniwget_private.h" />
<ClInclude Include="..\src\minixml.h" />
<ClInclude Include="..\include\portlistingparse.h" />
<ClInclude Include="..\src\receivedata.h" />
<ClInclude Include="..\include\upnpcommands.h" />
<ClInclude Include="..\include\upnpdev.h" />
<ClInclude Include="..\include\upnperrors.h" />
<ClInclude Include="..\include\upnpreplyparse.h" />
<ClInclude Include="..\src\win32_snprintf.h" />
<ClInclude Include="..\rc_version.h" />
</ItemGroup>
<ItemGroup>
<None Include="genminiupnpcstrings.vbs" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="../miniupnpc.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="..\connecthostport.c">
<Filter>sources</Filter>
</ClCompile>
<ClCompile Include="..\upnpreplyparse.c">
<Filter>sources</Filter>
</ClCompile>
<ClCompile Include="..\igd_desc_parse.c">
<Filter>sources</Filter>
</ClCompile>
<ClCompile Include="..\minisoap.c">
<Filter>sources</Filter>
</ClCompile>
<ClCompile Include="..\minissdpc.c">
<Filter>sources</Filter>
</ClCompile>
<ClCompile Include="..\miniupnpc.c">
<Filter>sources</Filter>
</ClCompile>
<ClCompile Include="..\miniwget.c">
<Filter>sources</Filter>
</ClCompile>
<ClCompile Include="..\minixml.c">
<Filter>sources</Filter>
</ClCompile>
<ClCompile Include="..\portlistingparse.c">
<Filter>sources</Filter>
</ClCompile>
<ClCompile Include="..\receivedata.c">
<Filter>sources</Filter>
</ClCompile>
<ClCompile Include="..\upnpcommands.c">
<Filter>sources</Filter>
</ClCompile>
<ClCompile Include="..\upnpdev.c">
<Filter>sources</Filter>
</ClCompile>
<ClCompile Include="..\upnperrors.c">
<Filter>sources</Filter>
</ClCompile>
<ClCompile Include="..\addr_is_reserved.c">
<Filter>sources</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\connecthostport.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\igd_desc_parse.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\minisoap.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\minissdpc.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\miniupnpc.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\miniupnpcstrings.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\miniupnpctypes.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\miniwget.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\minixml.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\portlistingparse.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\receivedata.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\upnpcommands.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\upnpdev.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\upnperrors.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\upnpreplyparse.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\miniupnpc_socketdef.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\miniupnpc_declspec.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\miniwget_private.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\addr_is_reserved.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\win32_snprintf.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\rc_version.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="sources">
<UniqueIdentifier>{f2cbd46b-f63f-412e-80e5-b7c9048a1add}</UniqueIdentifier>
</Filter>
<Filter Include="headers">
<UniqueIdentifier>{2b3996de-1bc4-418b-8a83-a5f34fdf0df5}</UniqueIdentifier>
</Filter>
<Filter Include="scripts">
<UniqueIdentifier>{45dbc39d-c3ca-4a75-adf0-76070e20415a}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="genminiupnpcstrings.vbs">
<Filter>scripts</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="../miniupnpc.rc">
<Filter>Fichiers de ressources</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,38 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25123.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc", "miniupnpc.vcxproj", "{D28CE435-CB33-4BAE-8A52-C6EF915956F5}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "upnpc-static", "upnpc-static.vcxproj", "{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug Dll|Win32 = Debug Dll|Win32
Debug|Win32 = Debug|Win32
Release Dll|Win32 = Release Dll|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug Dll|Win32.ActiveCfg = Debug Dll|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug Dll|Win32.Build.0 = Debug Dll|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.ActiveCfg = Debug|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.Build.0 = Debug|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release Dll|Win32.ActiveCfg = Release Dll|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release Dll|Win32.Build.0 = Release Dll|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.ActiveCfg = Release|Win32
{D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.Build.0 = Release|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug Dll|Win32.ActiveCfg = Debug Dll|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug Dll|Win32.Build.0 = Debug Dll|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.ActiveCfg = Debug|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.Build.0 = Debug|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release Dll|Win32.ActiveCfg = Release Dll|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release Dll|Win32.Build.0 = Release Dll|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.ActiveCfg = Release|Win32
{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,195 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="upnpc-static"
ProjectGUID="{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}"
RootNamespace="upnpcstatic"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="_DEBUG;_CONSOLE;MINIUPNP_STATICLIB;DEBUG;_CRT_SECURE_NO_WARNINGS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib IPHlpApi.Lib Debug\miniupnpc.lib"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
PreprocessorDefinitions="NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib IPHlpApi.Lib Release\miniupnpc.lib"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Fichiers sources"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\upnpc.c"
>
</File>
</Filter>
<Filter
Name="Fichiers d&apos;en-t<>te"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
</Filter>
<Filter
Name="Fichiers de ressources"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,184 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug Dll|Win32">
<Configuration>Debug Dll</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release Dll|Win32">
<Configuration>Release Dll</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}</ProjectGuid>
<RootNamespace>upnpcstatic</RootNamespace>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>14.0.25123.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
<LinkIncremental>true</LinkIncremental>
<TargetName>upnpc-shared</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
<LinkIncremental>false</LinkIncremental>
<TargetName>upnpc-shared</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;MINIUPNP_STATICLIB;DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;$(Configuration)\miniupnpc.lib;IPHlpApi.Lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;MINIUPNP_STATICLIB;DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;$(Configuration)\miniupnpc.lib;$(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;$(Configuration)\miniupnpc.lib;IPHlpApi.Lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;$(Configuration)\miniupnpc.lib;IPHlpApi.Lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\upnpc.c" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="miniupnpc.vcxproj">
<Project>{d28ce435-cb33-4bae-8a52-c6ef915956f5}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Fichiers sources">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Fichiers d%27en-tête">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Fichiers de ressources">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\upnpc.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,176 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug Dll|Win32">
<Configuration>Debug Dll</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release Dll|Win32">
<Configuration>Release Dll</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}</ProjectGuid>
<RootNamespace>upnpcstatic</RootNamespace>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">$(Configuration)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">true</LinkIncremental>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">$(Configuration)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">false</LinkIncremental>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">upnpc-static</TargetName>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">upnpc-static</TargetName>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">upnpc-static</TargetName>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">upnpc-static</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;MINIUPNP_STATICLIB;DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;IPHlpApi.Lib;$(Configuration)\miniupnpc.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;$(Configuration)\miniupnpc.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;IPHlpApi.Lib;$(Configuration)\miniupnpc.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;$(Configuration)\miniupnpc.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\upnpc.c" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="miniupnpc_vs2010.vcxproj">
<Project>{d28ce435-cb33-4bae-8a52-c6ef915956f5}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,177 @@
/* $Id: portlistingparse.c,v 1.12 2025/03/29 17:58:33 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* (c) 2011-2025 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <string.h>
#include <stdlib.h>
#ifdef DEBUG
#include <stdio.h>
#endif /* DEBUG */
#include "portlistingparse.h"
#include "minixml.h"
#if defined(__HAIKU__)
/* rename our private function because Haiku already defines a atoui() function */
#define atoui atoui2
#endif
/* list of the elements */
static const struct {
const portMappingElt code;
const char * const str;
} elements[] = {
{ PortMappingEntry, "PortMappingEntry"},
{ NewRemoteHost, "NewRemoteHost"},
{ NewExternalPort, "NewExternalPort"},
{ NewProtocol, "NewProtocol"},
{ NewInternalPort, "NewInternalPort"},
{ NewInternalClient, "NewInternalClient"},
{ NewEnabled, "NewEnabled"},
{ NewDescription, "NewDescription"},
{ NewLeaseTime, "NewLeaseTime"},
{ PortMappingEltNone, NULL}
};
/* Helper function */
static UNSIGNED_INTEGER
atoui(const char * p, int l)
{
UNSIGNED_INTEGER r = 0;
while(l > 0 && *p)
{
if(*p >= '0' && *p <= '9')
r = r*10 + (*p - '0');
else
break;
p++;
l--;
}
return r;
}
/* Start element handler */
static void
startelt(void * d, const char * name, int l)
{
int i;
struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
pdata->curelt = PortMappingEltNone;
for(i = 0; elements[i].str; i++)
{
if(strlen(elements[i].str) == (size_t)l && memcmp(name, elements[i].str, l) == 0)
{
pdata->curelt = elements[i].code;
break;
}
}
if(pdata->curelt == PortMappingEntry)
{
struct PortMapping * pm;
pm = calloc(1, sizeof(struct PortMapping));
if(pm == NULL)
{
/* malloc error */
#ifdef DEBUG
fprintf(stderr, "%s: error allocating memory",
"startelt");
#endif /* DEBUG */
return;
}
pm->l_next = pdata->l_head; /* insert in list */
pdata->l_head = pm;
}
}
/* End element handler */
static void
endelt(void * d, const char * name, int l)
{
struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
(void)name;
(void)l;
pdata->curelt = PortMappingEltNone;
}
/* Data handler */
static void
data(void * d, const char * data, int l)
{
struct PortMapping * pm;
struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
pm = pdata->l_head;
if(!pm)
return;
if(l > 63)
l = 63;
switch(pdata->curelt)
{
case NewRemoteHost:
memcpy(pm->remoteHost, data, l);
pm->remoteHost[l] = '\0';
break;
case NewExternalPort:
pm->externalPort = (unsigned short)atoui(data, l);
break;
case NewProtocol:
if(l > 3)
l = 3;
memcpy(pm->protocol, data, l);
pm->protocol[l] = '\0';
break;
case NewInternalPort:
pm->internalPort = (unsigned short)atoui(data, l);
break;
case NewInternalClient:
memcpy(pm->internalClient, data, l);
pm->internalClient[l] = '\0';
break;
case NewEnabled:
pm->enabled = (unsigned char)atoui(data, l);
break;
case NewDescription:
memcpy(pm->description, data, l);
pm->description[l] = '\0';
break;
case NewLeaseTime:
pm->leaseTime = atoui(data, l);
break;
default:
break;
}
}
/* Parse the PortMappingList XML document for IGD version 2
*/
void
ParsePortListing(const char * buffer, int bufsize,
struct PortMappingParserData * pdata)
{
struct xmlparser parser;
memset(pdata, 0, sizeof(struct PortMappingParserData));
/* init xmlparser */
parser.xmlstart = buffer;
parser.xmlsize = bufsize;
parser.data = pdata;
parser.starteltfunc = startelt;
parser.endeltfunc = endelt;
parser.datafunc = data;
parser.attfunc = 0;
parsexml(&parser);
}
void
FreePortListing(struct PortMappingParserData * pdata)
{
struct PortMapping * pm;
while((pm = pdata->l_head) != NULL)
{
/* remove from list */
pdata->l_head = pm->l_next;
free(pm);
}
}

View File

@@ -0,0 +1,91 @@
/* $Id: portlistingparse.h,v 1.12 2025/02/08 23:15:17 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2011-2025 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef PORTLISTINGPARSE_H_INCLUDED
#define PORTLISTINGPARSE_H_INCLUDED
/*! \file portlistingparse.h
* \brief Parsing of the list of port mappings
*
* As returned by GetListOfPortMappings.
* Sample of PortMappingEntry :
*
* <p:PortMappingEntry>
* <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
* <p:NewExternalPort>2345</p:NewExternalPort>
* <p:NewProtocol>TCP</p:NewProtocol>
* <p:NewInternalPort>2345</p:NewInternalPort>
* <p:NewInternalClient>192.168.1.137</p:NewInternalClient>
* <p:NewEnabled>1</p:NewEnabled>
* <p:NewDescription>dooom</p:NewDescription>
* <p:NewLeaseTime>345</p:NewLeaseTime>
* </p:PortMappingEntry>
*/
#include "miniupnpc_declspec.h"
/* for the definition of UNSIGNED_INTEGER */
#include "miniupnpctypes.h"
#ifdef __cplusplus
extern "C" {
#endif
/*!
* \brief enum of all XML elements
*/
typedef enum { PortMappingEltNone,
PortMappingEntry, NewRemoteHost,
NewExternalPort, NewProtocol,
NewInternalPort, NewInternalClient,
NewEnabled, NewDescription,
NewLeaseTime } portMappingElt;
/*!
* \brief linked list of port mappings
*/
struct PortMapping {
struct PortMapping * l_next; /*!< \brief next list element */
UNSIGNED_INTEGER leaseTime; /*!< \brief in seconds */
unsigned short externalPort; /*!< \brief external port */
unsigned short internalPort; /*!< \brief internal port */
char remoteHost[64]; /*!< \brief empty for wildcard */
char internalClient[64]; /*!< \brief internal IP address */
char description[64]; /*!< \brief description */
char protocol[4]; /*!< \brief `TCP` or `UDP` */
unsigned char enabled; /*!< \brief 0 (false) or 1 (true) */
};
/*!
* \brief structure for ParsePortListing()
*/
struct PortMappingParserData {
struct PortMapping * l_head; /*!< \brief list head */
portMappingElt curelt; /*!< \brief currently parsed element */
};
/*!
* \brief parse the NewPortListing part of GetListOfPortMappings response
*
* \param[in] buffer XML data
* \param[in] bufsize length of XML data
* \param[out] pdata Parsed data
*/
MINIUPNP_LIBSPEC void
ParsePortListing(const char * buffer, int bufsize,
struct PortMappingParserData * pdata);
/*!
* \brief free parsed data structure
*
* \param[in] pdata Parsed data to free
*/
MINIUPNP_LIBSPEC void
FreePortListing(struct PortMappingParserData * pdata);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,91 @@
#! /usr/bin/env python
# vim: tabstop=2 shiftwidth=2 expandtab
# MiniUPnP project
# Author : Thomas Bernard
# Python 3
# This Sample code is public domain.
# website : https://miniupnp.tuxfamily.org/
# import the python miniupnpc module
import miniupnpc
import sys
try:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-m', '--multicastif')
parser.add_argument('-p', '--minissdpdsocket')
parser.add_argument('-d', '--discoverdelay', type=int, default=200)
parser.add_argument('-z', '--localport', type=int, default=0)
# create the object
u = miniupnpc.UPnP(**vars(parser.parse_args()))
except:
print('argparse not available')
i = 1
multicastif = None
minissdpdsocket = None
discoverdelay = 200
localport = 0
while i < len(sys.argv):
print(sys.argv[i])
if sys.argv[i] == '-m' or sys.argv[i] == '--multicastif':
multicastif = sys.argv[i+1]
elif sys.argv[i] == '-p' or sys.argv[i] == '--minissdpdsocket':
minissdpdsocket = sys.argv[i+1]
elif sys.argv[i] == '-d' or sys.argv[i] == '--discoverdelay':
discoverdelay = int(sys.argv[i+1])
elif sys.argv[i] == '-z' or sys.argv[i] == '--localport':
localport = int(sys.argv[i+1])
else:
raise Exception('invalid argument %s' % sys.argv[i])
i += 2
# create the object
u = miniupnpc.UPnP(multicastif, minissdpdsocket, discoverdelay, localport)
print('inital(default) values :')
print(' discoverdelay', u.discoverdelay)
print(' lanaddr', u.lanaddr)
print(' multicastif', u.multicastif)
print(' minissdpdsocket', u.minissdpdsocket)
#u.minissdpdsocket = '../minissdpd/minissdpd.sock'
# discovery process, it usually takes several seconds (2 seconds or more)
print('Discovering... delay=%ums' % u.discoverdelay)
print('%d device(s) detected' % u.discover())
# select an igd
try:
u.selectigd()
except Exception as e:
print('Exception :', e)
sys.exit(1)
# it is also possible to pass the root description URL to u.selectigd() :
# u.selectigd('http://192.168.1.254:5678/desc/root')
# display information about the IGD and the internet connection
print('local ip address :', u.lanaddr)
print('external ip address :', u.externalipaddress())
print( u.statusinfo(), u.connectiontype())
print('total bytes : sent', u.totalbytesent(), 'received', u.totalbytereceived())
print('total packets : sent', u.totalpacketsent(), 'received', u.totalpacketreceived())
#print u.addportmapping(64000, 'TCP',
# '192.168.1.166', 63000, 'port mapping test', '')
#print u.deleteportmapping(64000, 'TCP')
port = 0
proto = 'UDP'
# list the redirections :
i = 0
while True:
p = u.getgenericportmapping(i)
if p==None:
break
print(i, p)
(port, proto, (ihost,iport), desc, c, d, e) = p
#print port, desc
i = i + 1
print(u.getspecificportmapping(port, proto))
try:
print(u.getportmappingnumberofentries())
except Exception as e:
print('GetPortMappingNumberOfEntries() is not supported :', e)

View File

@@ -0,0 +1,106 @@
/* $Id: receivedata.c,v 1.11 2025/05/25 21:56:49 nanard Exp $ */
/* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Author : Thomas Bernard
* Copyright (c) 2011-2025 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#else /* _WIN32 */
#include <unistd.h>
#if defined(__amigaos__) && !defined(__amigaos4__)
#define socklen_t int
#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
#include <sys/select.h>
#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
#include <sys/socket.h>
#include <netinet/in.h>
#if !defined(__amigaos__) && !defined(__amigaos4__)
#include <poll.h>
#endif /* !defined(__amigaos__) && !defined(__amigaos4__) */
#include <errno.h>
#define MINIUPNPC_IGNORE_EINTR
#endif /* _WIN32 */
#include "receivedata.h"
int
receivedata(SOCKET socket,
char * data, int length,
int timeout, unsigned int * scope_id)
{
#ifdef MINIUPNPC_GET_SRC_ADDR
struct sockaddr_storage src_addr;
socklen_t src_addr_len = sizeof(src_addr);
#endif /* MINIUPNPC_GET_SRC_ADDR */
int n;
#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
/* using poll */
struct pollfd fds[1]; /* for the poll */
#ifdef MINIUPNPC_IGNORE_EINTR
do {
#endif /* MINIUPNPC_IGNORE_EINTR */
fds[0].fd = socket;
fds[0].events = POLLIN;
n = poll(fds, 1, timeout);
#ifdef MINIUPNPC_IGNORE_EINTR
} while(n < 0 && errno == EINTR);
#endif /* MINIUPNPC_IGNORE_EINTR */
if(n < 0) {
PRINT_SOCKET_ERROR("poll");
return -1;
} else if(n == 0) {
/* timeout */
return 0;
}
#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
/* using select under _WIN32 and amigaos */
fd_set socketSet;
TIMEVAL timeval;
FD_ZERO(&socketSet);
FD_SET(socket, &socketSet);
timeval.tv_sec = timeout / 1000;
timeval.tv_usec = (timeout % 1000) * 1000;
n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
if(n < 0) {
PRINT_SOCKET_ERROR("select");
return -1;
} else if(n == 0) {
return 0;
}
#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
#ifdef MINIUPNPC_GET_SRC_ADDR
memset(&src_addr, 0, sizeof(src_addr));
n = recvfrom(socket, data, length, 0,
(struct sockaddr *)&src_addr, &src_addr_len);
#else /* MINIUPNPC_GET_SRC_ADDR */
n = recv(socket, data, length, 0);
#endif /* MINIUPNPC_GET_SRC_ADDR */
if(n<0) {
PRINT_SOCKET_ERROR("recv");
}
#ifdef MINIUPNPC_GET_SRC_ADDR
if (src_addr.ss_family == AF_INET6) {
const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr;
#ifdef DEBUG
printf("scope_id=%u\n", src_addr6->sin6_scope_id);
#endif /* DEBUG */
if(scope_id)
*scope_id = src_addr6->sin6_scope_id;
} else {
if(scope_id)
*scope_id = 0;
}
#else /* MINIUPNPC_GET_SRC_ADDR */
if(scope_id)
*scope_id = 0;
#endif /* MINIUPNPC_GET_SRC_ADDR */
return n;
}

View File

@@ -0,0 +1,21 @@
/* $Id: receivedata.h,v 1.5 2018/04/06 10:53:15 nanard Exp $ */
/* Project: miniupnp
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author: Thomas Bernard
* Copyright (c) 2011-2018 Thomas Bernard
* This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */
#ifndef RECEIVEDATA_H_INCLUDED
#define RECEIVEDATA_H_INCLUDED
#include "miniupnpc_socketdef.h"
/* Reads data from the specified socket.
* Returns the number of bytes read if successful, zero if no bytes were
* read or if we timed out. Returns negative if there was an error. */
int receivedata(SOCKET socket,
char * data, int length,
int timeout, unsigned int * scope_id);
#endif

View File

@@ -0,0 +1,37 @@
#! /usr/bin/env python
# vim: tabstop=8 shiftwidth=8 expandtab
# $Id: setup.py,v 1.17 2024/06/22 19:23:20 nanard Exp $
# the MiniUPnP Project (c) 2007-2024 Thomas Bernard
# https://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
#
# python script to build the miniupnpc module under unix
#
# Uses MAKE environment variable (defaulting to 'make')
from setuptools import setup, Extension
from setuptools.command import build_ext
import subprocess
import os
EXT = ['build/libminiupnpc.a']
class make_then_build_ext(build_ext.build_ext):
def run(self):
subprocess.check_call([os.environ.get('MAKE', 'make')] + EXT)
build_ext.build_ext.run(self)
setup(name="miniupnpc",
version=open('VERSION').read().strip(),
author='Thomas BERNARD',
author_email='miniupnp@free.fr',
license=open('LICENSE').read(),
url='https://miniupnp.tuxfamily.org/',
description='MiniUPnP IGD client',
long_description=open('DESCRIPTION').read().strip(),
long_description_content_type='text/plain',
cmdclass={'build_ext': make_then_build_ext},
ext_modules=[
Extension(name="miniupnpc", sources=["src/miniupnpcmodule.c"],
include_dirs=['include'], extra_objects=EXT)
])

View File

@@ -0,0 +1,43 @@
#! /usr/bin/env python
# vim: tabstop=8 shiftwidth=8 expandtab
# $Id: setupmingw32.py,v 1.16 2024/06/22 19:23:20 nanard Exp $
# the MiniUPnP Project (c) 2007-2024 Thomas Bernard
# https://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
#
# python script to build the miniupnpc module under windows (using mingw32)
#
import sys
if (sys.version_info.major * 10 + sys.version_info.minor) >= 35:
compat_lib = ["legacy_stdio_definitions"]
else:
compat_lib = []
try:
from setuptools import setup, Extension
except ImportError:
from distutils.core import setup, Extension
from distutils import sysconfig
sysconfig.get_config_vars()["OPT"] = ''
sysconfig.get_config_vars()["CFLAGS"] = ''
lic = open('LICENSE').read()
# follow the symbolic link manually if needed
if lic.startswith('../'):
lic = open(lic).read()
setup(name="miniupnpc",
version=open('VERSION').read().strip(),
author='Thomas BERNARD',
author_email='miniupnp@free.fr',
license=lic,
url='https://miniupnp.tuxfamily.org/',
description='MiniUPnP IGD client',
long_description=open('DESCRIPTION').read().strip(),
long_description_content_type='text/plain',
ext_modules=[
Extension(name="miniupnpc", sources=["src/miniupnpcmodule.c"],
libraries=["ws2_32", "iphlpapi"] + compat_lib,
include_dirs=['include'], extra_objects=["miniupnpc.lib"])
])

View File

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

View File

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

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

View File

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

View File

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

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

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

View File

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

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)
snprintf(portstr, sizeof(portstr), ":%hu", port);
headerssize = snprintf(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);
}

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

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

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

View File

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

View File

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

View File

@@ -0,0 +1,629 @@
/* $Id: miniwget.c,v 1.88 2025/05/25 21:56:49 nanard Exp $ */
/* Project : miniupnp
* Website : 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. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.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 socklen_t int
#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 */
#else /* #ifdef _WIN32 */
#include <unistd.h>
#include <sys/param.h>
#if defined(__amigaos__) && !defined(__amigaos4__)
#define socklen_t int
#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
#include <sys/select.h>
#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#define closesocket close
#include <strings.h>
#endif /* #else _WIN32 */
#ifdef __GNU__
#define MAXHOSTNAMELEN 64
#endif /* __GNU__ */
#ifndef MIN
#define MIN(x,y) (((x)<(y))?(x):(y))
#endif /* MIN */
#include "miniupnpcstrings.h"
#include "miniwget.h"
#include "connecthostport.h"
#include "receivedata.h"
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif
/*
* Read a HTTP response from a socket.
* Process Content-Length and Transfer-encoding headers.
* return a pointer to the content buffer, which length is saved
* to the length parameter.
*/
void *
getHTTPResponse(SOCKET s, int * size, int * status_code)
{
char buf[2048];
int n;
int endofheaders = 0;
int chunked = 0;
int content_length = -1;
unsigned int chunksize = 0;
unsigned int bytestocopy = 0;
/* buffers : */
char * header_buf;
unsigned int header_buf_len = 2048;
unsigned int header_buf_used = 0;
char * content_buf;
unsigned int content_buf_len = 2048;
unsigned int content_buf_used = 0;
char chunksize_buf[32];
unsigned int chunksize_buf_index;
#ifdef DEBUG
char * reason_phrase = NULL;
int reason_phrase_len = 0;
#endif
if(status_code) *status_code = -1;
header_buf = malloc(header_buf_len);
if(header_buf == NULL)
{
#ifdef DEBUG
fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
#endif /* DEBUG */
*size = -1;
return NULL;
}
content_buf = malloc(content_buf_len);
if(content_buf == NULL)
{
free(header_buf);
#ifdef DEBUG
fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
#endif /* DEBUG */
*size = -1;
return NULL;
}
chunksize_buf[0] = '\0';
chunksize_buf_index = 0;
while((n = receivedata(s, buf, sizeof(buf), 5000, NULL)) > 0)
{
if(endofheaders == 0)
{
int i;
int linestart=0;
int colon=0;
int valuestart=0;
if(header_buf_used + n > header_buf_len) {
char * tmp = realloc(header_buf, header_buf_used + n);
if(tmp == NULL) {
/* memory allocation error */
free(header_buf);
free(content_buf);
*size = -1;
return NULL;
}
header_buf = tmp;
header_buf_len = header_buf_used + n;
}
memcpy(header_buf + header_buf_used, buf, n);
header_buf_used += n;
/* search for CR LF CR LF (end of headers)
* recognize also LF LF */
i = 0;
while(i < ((int)header_buf_used-1) && (endofheaders == 0)) {
if(header_buf[i] == '\r') {
i++;
if(header_buf[i] == '\n') {
i++;
if(i < (int)header_buf_used && header_buf[i] == '\r') {
i++;
if(i < (int)header_buf_used && header_buf[i] == '\n') {
endofheaders = i+1;
}
}
}
} else if(header_buf[i] == '\n') {
i++;
if(header_buf[i] == '\n') {
endofheaders = i+1;
}
}
i++;
}
if(endofheaders == 0)
continue;
/* parse header lines */
for(i = 0; i < endofheaders - 1; i++) {
if(linestart > 0 && colon <= linestart && header_buf[i]==':')
{
colon = i;
while(i < (endofheaders-1)
&& (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
i++;
valuestart = i + 1;
}
/* detecting end of line */
else if(header_buf[i]=='\r' || header_buf[i]=='\n')
{
if(linestart == 0 && status_code)
{
/* Status line
* HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
int sp;
for(sp = 0; sp < i - 1; sp++)
if(header_buf[sp] == ' ')
{
if(*status_code < 0)
{
if (header_buf[sp+1] >= '1' && header_buf[sp+1] <= '9')
*status_code = atoi(header_buf + sp + 1);
}
else
{
#ifdef DEBUG
reason_phrase = header_buf + sp + 1;
reason_phrase_len = i - sp - 1;
#endif
break;
}
}
#ifdef DEBUG
printf("HTTP status code = %d, Reason phrase = %.*s\n",
*status_code, reason_phrase_len, reason_phrase);
#endif
}
else if(colon > linestart && valuestart > colon)
{
#ifdef DEBUG
printf("header='%.*s', value='%.*s'\n",
colon-linestart, header_buf+linestart,
i-valuestart, header_buf+valuestart);
#endif
if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
{
content_length = atoi(header_buf+valuestart);
#ifdef DEBUG
printf("Content-Length: %d\n", content_length);
#endif
}
else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
&& 0==strncasecmp(header_buf+valuestart, "chunked", 7))
{
#ifdef DEBUG
printf("chunked transfer-encoding!\n");
#endif
chunked = 1;
}
}
while((i < (int)header_buf_used) && (header_buf[i]=='\r' || header_buf[i] == '\n'))
i++;
linestart = i;
colon = linestart;
valuestart = 0;
}
}
/* copy the remaining of the received data back to buf */
n = header_buf_used - endofheaders;
memcpy(buf, header_buf + endofheaders, n);
/* if(headers) */
}
/* if we get there, endofheaders != 0.
* In the other case, there was a continue above */
/* content */
if(chunked)
{
int i = 0;
while(i < n)
{
if(chunksize == 0)
{
/* reading chunk size */
if(chunksize_buf_index == 0) {
/* skipping any leading CR LF */
if(buf[i] == '\r') i++;
if(i<n && buf[i] == '\n') i++;
}
while(i<n && isxdigit(buf[i])
&& chunksize_buf_index < (sizeof(chunksize_buf)-1))
{
chunksize_buf[chunksize_buf_index++] = buf[i];
chunksize_buf[chunksize_buf_index] = '\0';
i++;
}
while(i<n && buf[i] != '\r' && buf[i] != '\n')
i++; /* discarding chunk-extension */
if(i<n && buf[i] == '\r') i++;
if(i<n && buf[i] == '\n') {
unsigned int j;
for(j = 0; j < chunksize_buf_index; j++) {
if(chunksize_buf[j] >= '0'
&& chunksize_buf[j] <= '9')
chunksize = (chunksize << 4) + (chunksize_buf[j] - '0');
else
chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10);
}
chunksize_buf[0] = '\0';
chunksize_buf_index = 0;
i++;
} else {
/* not finished to get chunksize */
continue;
}
#ifdef DEBUG
printf("chunksize = %u (%x)\n", chunksize, chunksize);
#endif
if(chunksize == 0)
{
#ifdef DEBUG
printf("end of HTTP content - %d %d\n", i, n);
/*printf("'%.*s'\n", n-i, buf+i);*/
#endif
goto end_of_stream;
}
}
/* it is guaranteed that (n >= i) */
bytestocopy = (chunksize < (unsigned int)(n - i))?chunksize:(unsigned int)(n - i);
if((content_buf_used + bytestocopy) > content_buf_len)
{
char * tmp;
if((content_length >= 0) && ((unsigned int)content_length >= (content_buf_used + bytestocopy))) {
content_buf_len = content_length;
} else {
content_buf_len = content_buf_used + bytestocopy;
}
tmp = realloc(content_buf, content_buf_len);
if(tmp == NULL) {
/* memory allocation error */
free(content_buf);
free(header_buf);
*size = -1;
return NULL;
}
content_buf = tmp;
}
memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
content_buf_used += bytestocopy;
i += bytestocopy;
chunksize -= bytestocopy;
}
}
else
{
/* not chunked */
if(content_length > 0
&& (content_buf_used + n) > (unsigned int)content_length) {
/* skipping additional bytes */
n = content_length - content_buf_used;
}
if(content_buf_used + n > content_buf_len)
{
char * tmp;
if(content_length >= 0
&& (unsigned int)content_length >= (content_buf_used + n)) {
content_buf_len = content_length;
} else {
content_buf_len = content_buf_used + n;
}
tmp = realloc(content_buf, content_buf_len);
if(tmp == NULL) {
/* memory allocation error */
free(content_buf);
free(header_buf);
*size = -1;
return NULL;
}
content_buf = tmp;
}
memcpy(content_buf + content_buf_used, buf, n);
content_buf_used += n;
}
/* use the Content-Length header value if available */
if(content_length > 0 && content_buf_used >= (unsigned int)content_length)
{
#ifdef DEBUG
printf("End of HTTP content\n");
#endif
break;
}
}
end_of_stream:
free(header_buf);
*size = content_buf_used;
if(content_buf_used == 0)
{
free(content_buf);
content_buf = NULL;
}
return content_buf;
}
/* miniwget3() :
* do all the work.
* Return NULL if something failed. */
static void *
miniwget3(const char * host,
unsigned short port, const char * path,
int * size, char * addr_str, int addr_str_len,
const char * httpversion, unsigned int scope_id,
int * status_code)
{
char buf[2048];
SOCKET s;
int n;
int len;
int sent;
void * content;
*size = 0;
s = connecthostport(host, port, scope_id);
if(ISINVALID(s))
return NULL;
/* get address for caller ! */
if(addr_str)
{
struct sockaddr_storage saddr;
socklen_t saddrlen;
saddrlen = sizeof(saddr);
if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0)
{
perror("getsockname");
}
else
{
#if defined(__amigaos__) && !defined(__amigaos4__)
/* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
* But his function make a string with the port : nn.nn.nn.nn:port */
/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
NULL, addr_str, (DWORD *)&addr_str_len))
{
printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
}*/
/* the following code is only compatible with ip v4 addresses */
strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
#else
#if 0
if(saddr.sa_family == AF_INET6) {
inet_ntop(AF_INET6,
&(((struct sockaddr_in6 *)&saddr)->sin6_addr),
addr_str, addr_str_len);
} else {
inet_ntop(AF_INET,
&(((struct sockaddr_in *)&saddr)->sin_addr),
addr_str, addr_str_len);
}
#endif
/* getnameinfo return ip v6 address with the scope identifier
* such as : 2a01:e35:8b2b:7330::%4281128194 */
n = getnameinfo((const struct sockaddr *)&saddr, saddrlen,
addr_str, addr_str_len,
NULL, 0,
NI_NUMERICHOST | NI_NUMERICSERV);
if(n != 0) {
#ifdef _WIN32
fprintf(stderr, "getnameinfo() failed : %d\n", n);
#else
fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
#endif
}
#endif
}
#ifdef DEBUG
printf("address miniwget : %s\n", addr_str);
#endif
}
len = snprintf(buf, sizeof(buf),
"GET %s HTTP/%s\r\n"
"Host: %s:%d\r\n"
"Connection: close\r\n"
"User-Agent: " OS_STRING " " UPNP_VERSION_STRING " MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
"\r\n",
path, httpversion, host, port);
if ((unsigned int)len >= sizeof(buf))
{
closesocket(s);
return NULL;
}
sent = 0;
/* sending the HTTP request */
while(sent < len)
{
n = send(s, buf+sent, len-sent, 0);
if(n < 0)
{
perror("send");
closesocket(s);
return NULL;
}
else
{
sent += n;
}
}
content = getHTTPResponse(s, size, status_code);
closesocket(s);
return content;
}
/* parseURL()
* arguments :
* url : source string not modified
* hostname : hostname destination string (size of MAXHOSTNAMELEN+1)
* port : port (destination)
* path : pointer to the path part of the URL
*
* Return values :
* 0 - Failure
* 1 - Success */
int
parseURL(const char * url,
char * hostname, unsigned short * port,
char * * path, unsigned int * scope_id)
{
char * p1, *p2, *p3;
if(!url)
return 0;
p1 = strstr(url, "://");
if(!p1)
return 0;
p1 += 3;
if( (url[0]!='h') || (url[1]!='t')
||(url[2]!='t') || (url[3]!='p'))
return 0;
memset(hostname, 0, MAXHOSTNAMELEN + 1);
if(*p1 == '[')
{
/* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
char * scope;
scope = strchr(p1, '%');
p2 = strchr(p1, ']');
if(p2 && scope && scope < p2 && scope_id) {
/* parse scope */
#ifdef IF_NAMESIZE
char tmp[IF_NAMESIZE];
int l;
scope++;
/* "%25" is just '%' in URL encoding */
if(scope[0] == '2' && scope[1] == '5')
scope += 2; /* skip "25" */
l = p2 - scope;
if(l >= IF_NAMESIZE)
l = IF_NAMESIZE - 1;
memcpy(tmp, scope, l);
tmp[l] = '\0';
*scope_id = if_nametoindex(tmp);
if(*scope_id == 0) {
*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
}
#else
/* under windows, scope is numerical */
char tmp[8];
size_t l;
scope++;
/* "%25" is just '%' in URL encoding */
if(scope[0] == '2' && scope[1] == '5')
scope += 2; /* skip "25" */
l = p2 - scope;
if(l >= sizeof(tmp))
l = sizeof(tmp) - 1;
memcpy(tmp, scope, l);
tmp[l] = '\0';
*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
#endif
}
p3 = strchr(p1, '/');
if(p2 && p3)
{
p2++;
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
if(*p2 == ':')
{
*port = 0;
p2++;
while( (*p2 >= '0') && (*p2 <= '9'))
{
*port *= 10;
*port += (unsigned short)(*p2 - '0');
p2++;
}
}
else
{
*port = 80;
}
*path = p3;
return 1;
}
}
p2 = strchr(p1, ':');
p3 = strchr(p1, '/');
if(!p3)
return 0;
if(!p2 || (p2>p3))
{
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
*port = 80;
}
else
{
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
*port = 0;
p2++;
while( (*p2 >= '0') && (*p2 <= '9'))
{
*port *= 10;
*port += (unsigned short)(*p2 - '0');
p2++;
}
}
*path = p3;
return 1;
}
void *
miniwget(const char * url, int * size,
unsigned int scope_id, int * status_code)
{
unsigned short port;
char * path;
/* protocol://host:port/chemin */
char hostname[MAXHOSTNAMELEN+1];
*size = 0;
if(!parseURL(url, hostname, &port, &path, &scope_id))
return NULL;
#ifdef DEBUG
printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
hostname, port, path, scope_id);
#endif
return miniwget3(hostname, port, path, size, 0, 0, "1.1", scope_id, status_code);
}
void *
miniwget_getaddr(const char * url, int * size,
char * addr, int addrlen, unsigned int scope_id,
int * status_code)
{
unsigned short port;
char * path;
/* protocol://host:port/path */
char hostname[MAXHOSTNAMELEN+1];
*size = 0;
if(addr)
addr[0] = '\0';
if(!parseURL(url, hostname, &port, &path, &scope_id))
return NULL;
#ifdef DEBUG
printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
hostname, port, path, scope_id);
#endif
return miniwget3(hostname, port, path, size, addr, addrlen, "1.1", scope_id, status_code);
}

View File

@@ -0,0 +1,54 @@
/* $Id: miniwget_private.h,v 1.2 2025/03/02 01:14:38 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2018-2025 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
#ifndef MINIWGET_INTERNAL_H_INCLUDED
#define MINIWGET_INTERNAL_H_INCLUDED
/*! \file miniwget_private.h
* \brief Lightweight HTTP client private API
*/
#include "miniupnpc_socketdef.h"
/*! \brief Read a HTTP response from a socket
*
* Processed HTTP headers :
* - `Content-Length`
* - `Transfer-encoding`
* return a pointer to the content buffer, which length is saved
* to the length parameter.
* \param[in] s socket
* \param[out] size returned content buffer size
* \param[out] status_code HTTP Status code
* \return malloc'ed content buffer
*/
void * getHTTPResponse(SOCKET s, int * size, int * status_code);
/*! \brief parse a HTTP URL
*
* URL formats supported :
* - `http://192.168.1.1/path/xxx`
* - `http://192.168.1.1:8080/path/xxx`
* - `http://[2a00:1234:5678:90ab::123]/path/xxx`
* - `http://[2a00:1234:5678:90ab::123]:8080/path/xxx`
* - `http://[fe80::1234:5678:90ab%%eth0]/path/xxx`
* - `http://[fe80::1234:5678:90ab%%eth0]:8080/path/xxx`
*
* `%` may be encoded as `%25`
*
* \param[in] url URL to parse
* \param[out] hostname hostname part of the URL (size of MAXHOSTNAMELEN+1)
* \param[out] port set to the port specified in the URL or 80
* \param[out] path set to the begining of the path part of the URL
* \param[out] scope_id set to the interface id if specified in the
* link-local IPv6 address
* \return 0 for failure, 1 for success
*/
int parseURL(const char * url,
char * hostname, unsigned short * port, char * * path,
unsigned int * scope_id);
#endif

View File

@@ -0,0 +1,231 @@
/* $Id: minixml.c,v 1.12 2017/12/12 11:17:40 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* minixml.c : the minimum size a xml parser can be ! */
/* Project : miniupnp
* webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
Copyright (c) 2005-2017, 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:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* 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.
* The name of the author may not 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 OWNER 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.
*/
#include <string.h>
#include "minixml.h"
/* parseatt : used to parse the argument list
* return 0 (false) in case of success and -1 (true) if the end
* of the xmlbuffer is reached. */
static int parseatt(struct xmlparser * p)
{
const char * attname;
int attnamelen;
const char * attvalue;
int attvaluelen;
while(p->xml < p->xmlend)
{
if(*p->xml=='/' || *p->xml=='>')
return 0;
if( !IS_WHITE_SPACE(*p->xml) )
{
char sep;
attname = p->xml;
attnamelen = 0;
while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) )
{
attnamelen++; p->xml++;
if(p->xml >= p->xmlend)
return -1;
}
while(*(p->xml++) != '=')
{
if(p->xml >= p->xmlend)
return -1;
}
while(IS_WHITE_SPACE(*p->xml))
{
p->xml++;
if(p->xml >= p->xmlend)
return -1;
}
sep = *p->xml;
if(sep=='\'' || sep=='\"')
{
p->xml++;
if(p->xml >= p->xmlend)
return -1;
attvalue = p->xml;
attvaluelen = 0;
while(*p->xml != sep)
{
attvaluelen++; p->xml++;
if(p->xml >= p->xmlend)
return -1;
}
}
else
{
attvalue = p->xml;
attvaluelen = 0;
while( !IS_WHITE_SPACE(*p->xml)
&& *p->xml != '>' && *p->xml != '/')
{
attvaluelen++; p->xml++;
if(p->xml >= p->xmlend)
return -1;
}
}
/*printf("%.*s='%.*s'\n",
attnamelen, attname, attvaluelen, attvalue);*/
if(p->attfunc)
p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen);
}
p->xml++;
}
return -1;
}
/* parseelt parse the xml stream and
* call the callback functions when needed... */
static void parseelt(struct xmlparser * p)
{
int i;
const char * elementname;
while(p->xml < (p->xmlend - 1))
{
if((p->xml + 4) <= p->xmlend && (0 == memcmp(p->xml, "<!--", 4)))
{
p->xml += 3;
/* ignore comments */
do
{
p->xml++;
if ((p->xml + 3) >= p->xmlend)
return;
}
while(memcmp(p->xml, "-->", 3) != 0);
p->xml += 3;
}
else if((p->xml)[0]=='<' && (p->xml)[1]!='?')
{
i = 0; elementname = ++p->xml;
while( !IS_WHITE_SPACE(*p->xml)
&& (*p->xml!='>') && (*p->xml!='/')
)
{
i++; p->xml++;
if (p->xml >= p->xmlend)
return;
/* to ignore namespace : */
if(*p->xml==':')
{
i = 0;
elementname = ++p->xml;
}
}
if(i>0)
{
if(p->starteltfunc)
p->starteltfunc(p->data, elementname, i);
if(parseatt(p))
return;
if(*p->xml!='/')
{
const char * data;
i = 0; data = ++p->xml;
if (p->xml >= p->xmlend)
return;
while( IS_WHITE_SPACE(*p->xml) )
{
i++; p->xml++;
if (p->xml >= p->xmlend)
return;
}
/* CDATA are at least 9 + 3 characters long : <![CDATA[ ]]> */
if((p->xmlend >= (p->xml + (9 + 3))) && (memcmp(p->xml, "<![CDATA[", 9) == 0))
{
/* CDATA handling */
p->xml += 9;
data = p->xml;
i = 0;
while(memcmp(p->xml, "]]>", 3) != 0)
{
i++; p->xml++;
if ((p->xml + 3) >= p->xmlend)
return;
}
if(i>0 && p->datafunc)
p->datafunc(p->data, data, i);
while(*p->xml!='<')
{
p->xml++;
if (p->xml >= p->xmlend)
return;
}
}
else
{
while(*p->xml!='<')
{
i++; p->xml++;
if ((p->xml + 1) >= p->xmlend)
return;
}
if(i>0 && p->datafunc && *(p->xml + 1) == '/')
p->datafunc(p->data, data, i);
}
}
}
else if(*p->xml == '/')
{
i = 0; elementname = ++p->xml;
if (p->xml >= p->xmlend)
return;
while((*p->xml != '>'))
{
i++; p->xml++;
if (p->xml >= p->xmlend)
return;
}
if(p->endeltfunc)
p->endeltfunc(p->data, elementname, i);
p->xml++;
}
}
else
{
p->xml++;
}
}
}
/* the parser must be initialized before calling this function */
void parsexml(struct xmlparser * parser)
{
parser->xml = parser->xmlstart;
parser->xmlend = parser->xmlstart + parser->xmlsize;
parseelt(parser);
}

View File

@@ -0,0 +1,37 @@
/* $Id: minixml.h,v 1.8 2019/02/10 12:29:25 nanard Exp $ */
/* minimal xml parser
*
* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Author : Thomas Bernard
* Copyright (c) 2005 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
#ifndef MINIXML_H_INCLUDED
#define MINIXML_H_INCLUDED
#define IS_WHITE_SPACE(c) ((c)==' ' || (c)=='\t' || (c)=='\r' || (c)=='\n')
/* if a callback function pointer is set to NULL,
* the function is not called */
struct xmlparser {
const char *xmlstart;
const char *xmlend;
const char *xml; /* pointer to current character */
int xmlsize;
void * data;
void (*starteltfunc) (void *, const char *, int);
void (*endeltfunc) (void *, const char *, int);
void (*datafunc) (void *, const char *, int);
void (*attfunc) (void *, const char *, int, const char *, int);
};
/* parsexml()
* the xmlparser structure must be initialized before the call
* the following structure members have to be initialized :
* xmlstart, xmlsize, data, *func
* xml is for internal usage, xmlend is computed automatically */
void parsexml(struct xmlparser *);
#endif

View File

@@ -0,0 +1,163 @@
/* $Id: minixmlvalid.c,v 1.7 2015/07/15 12:41:15 nanard Exp $ */
/* MiniUPnP Project
* http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
* minixmlvalid.c :
* validation program for the minixml parser
*
* (c) 2006-2011 Thomas Bernard */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "minixml.h"
/* xml event structure */
struct event {
enum { ELTSTART, ELTEND, ATT, CHARDATA } type;
const char * data;
int len;
};
struct eventlist {
int n;
struct event * events;
};
/* compare 2 xml event lists
* return 0 if the two lists are equals */
int evtlistcmp(struct eventlist * a, struct eventlist * b)
{
int i;
struct event * ae, * be;
if(a->n != b->n)
{
printf("event number not matching : %d != %d\n", a->n, b->n);
/*return 1;*/
}
for(i=0; i<a->n; i++)
{
ae = a->events + i;
be = b->events + i;
if( (ae->type != be->type)
||(ae->len != be->len)
||memcmp(ae->data, be->data, ae->len))
{
printf("Found a difference : %d '%.*s' != %d '%.*s'\n",
ae->type, ae->len, ae->data,
be->type, be->len, be->data);
return 1;
}
}
return 0;
}
/* Test data */
static const char xmldata[] =
"<xmlroot>\n"
" <elt1 att1=\"attvalue1\" att2=\"attvalue2\">"
"character data"
"</elt1> \n \t"
"<elt1b/>"
"<elt1>\n<![CDATA[ <html>stuff !\n ]]> \n</elt1>\n"
"<elt2a> \t<elt2b>chardata1</elt2b><elt2b> chardata2 </elt2b></elt2a>"
"</xmlroot>";
static const struct event evtref[] =
{
{ELTSTART, "xmlroot", 7},
{ELTSTART, "elt1", 4},
/* attributes */
{CHARDATA, "character data", 14},
{ELTEND, "elt1", 4},
{ELTSTART, "elt1b", 5},
{ELTSTART, "elt1", 4},
{CHARDATA, " <html>stuff !\n ", 16},
{ELTEND, "elt1", 4},
{ELTSTART, "elt2a", 5},
{ELTSTART, "elt2b", 5},
{CHARDATA, "chardata1", 9},
{ELTEND, "elt2b", 5},
{ELTSTART, "elt2b", 5},
{CHARDATA, " chardata2 ", 11},
{ELTEND, "elt2b", 5},
{ELTEND, "elt2a", 5},
{ELTEND, "xmlroot", 7}
};
void startelt(void * data, const char * p, int l)
{
struct eventlist * evtlist = data;
struct event * evt;
evt = evtlist->events + evtlist->n;
/*printf("startelt : %.*s\n", l, p);*/
evt->type = ELTSTART;
evt->data = p;
evt->len = l;
evtlist->n++;
}
void endelt(void * data, const char * p, int l)
{
struct eventlist * evtlist = data;
struct event * evt;
evt = evtlist->events + evtlist->n;
/*printf("endelt : %.*s\n", l, p);*/
evt->type = ELTEND;
evt->data = p;
evt->len = l;
evtlist->n++;
}
void chardata(void * data, const char * p, int l)
{
struct eventlist * evtlist = data;
struct event * evt;
evt = evtlist->events + evtlist->n;
/*printf("chardata : '%.*s'\n", l, p);*/
evt->type = CHARDATA;
evt->data = p;
evt->len = l;
evtlist->n++;
}
int testxmlparser(const char * xml, int size)
{
int r;
struct eventlist evtlist;
struct eventlist evtlistref;
struct xmlparser parser;
evtlist.n = 0;
evtlist.events = malloc(sizeof(struct event)*100);
if(evtlist.events == NULL)
{
fprintf(stderr, "Memory allocation error.\n");
return -1;
}
memset(&parser, 0, sizeof(parser));
parser.xmlstart = xml;
parser.xmlsize = size;
parser.data = &evtlist;
parser.starteltfunc = startelt;
parser.endeltfunc = endelt;
parser.datafunc = chardata;
parsexml(&parser);
printf("%d events\n", evtlist.n);
/* compare */
evtlistref.n = sizeof(evtref)/sizeof(struct event);
evtlistref.events = (struct event *)evtref;
r = evtlistcmp(&evtlistref, &evtlist);
free(evtlist.events);
return r;
}
int main(int argc, char * * argv)
{
int r;
(void)argc; (void)argv;
r = testxmlparser(xmldata, sizeof(xmldata)-1);
if(r)
printf("minixml validation test failed\n");
return r;
}

View File

@@ -0,0 +1,177 @@
/* $Id: portlistingparse.c,v 1.12 2025/03/29 17:58:33 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* (c) 2011-2025 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <string.h>
#include <stdlib.h>
#ifdef DEBUG
#include <stdio.h>
#endif /* DEBUG */
#include "portlistingparse.h"
#include "minixml.h"
#if defined(__HAIKU__)
/* rename our private function because Haiku already defines a atoui() function */
#define atoui atoui2
#endif
/* list of the elements */
static const struct {
const portMappingElt code;
const char * const str;
} elements[] = {
{ PortMappingEntry, "PortMappingEntry"},
{ NewRemoteHost, "NewRemoteHost"},
{ NewExternalPort, "NewExternalPort"},
{ NewProtocol, "NewProtocol"},
{ NewInternalPort, "NewInternalPort"},
{ NewInternalClient, "NewInternalClient"},
{ NewEnabled, "NewEnabled"},
{ NewDescription, "NewDescription"},
{ NewLeaseTime, "NewLeaseTime"},
{ PortMappingEltNone, NULL}
};
/* Helper function */
static UNSIGNED_INTEGER
atoui(const char * p, int l)
{
UNSIGNED_INTEGER r = 0;
while(l > 0 && *p)
{
if(*p >= '0' && *p <= '9')
r = r*10 + (*p - '0');
else
break;
p++;
l--;
}
return r;
}
/* Start element handler */
static void
startelt(void * d, const char * name, int l)
{
int i;
struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
pdata->curelt = PortMappingEltNone;
for(i = 0; elements[i].str; i++)
{
if(strlen(elements[i].str) == (size_t)l && memcmp(name, elements[i].str, l) == 0)
{
pdata->curelt = elements[i].code;
break;
}
}
if(pdata->curelt == PortMappingEntry)
{
struct PortMapping * pm;
pm = calloc(1, sizeof(struct PortMapping));
if(pm == NULL)
{
/* malloc error */
#ifdef DEBUG
fprintf(stderr, "%s: error allocating memory",
"startelt");
#endif /* DEBUG */
return;
}
pm->l_next = pdata->l_head; /* insert in list */
pdata->l_head = pm;
}
}
/* End element handler */
static void
endelt(void * d, const char * name, int l)
{
struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
(void)name;
(void)l;
pdata->curelt = PortMappingEltNone;
}
/* Data handler */
static void
data(void * d, const char * data, int l)
{
struct PortMapping * pm;
struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
pm = pdata->l_head;
if(!pm)
return;
if(l > 63)
l = 63;
switch(pdata->curelt)
{
case NewRemoteHost:
memcpy(pm->remoteHost, data, l);
pm->remoteHost[l] = '\0';
break;
case NewExternalPort:
pm->externalPort = (unsigned short)atoui(data, l);
break;
case NewProtocol:
if(l > 3)
l = 3;
memcpy(pm->protocol, data, l);
pm->protocol[l] = '\0';
break;
case NewInternalPort:
pm->internalPort = (unsigned short)atoui(data, l);
break;
case NewInternalClient:
memcpy(pm->internalClient, data, l);
pm->internalClient[l] = '\0';
break;
case NewEnabled:
pm->enabled = (unsigned char)atoui(data, l);
break;
case NewDescription:
memcpy(pm->description, data, l);
pm->description[l] = '\0';
break;
case NewLeaseTime:
pm->leaseTime = atoui(data, l);
break;
default:
break;
}
}
/* Parse the PortMappingList XML document for IGD version 2
*/
void
ParsePortListing(const char * buffer, int bufsize,
struct PortMappingParserData * pdata)
{
struct xmlparser parser;
memset(pdata, 0, sizeof(struct PortMappingParserData));
/* init xmlparser */
parser.xmlstart = buffer;
parser.xmlsize = bufsize;
parser.data = pdata;
parser.starteltfunc = startelt;
parser.endeltfunc = endelt;
parser.datafunc = data;
parser.attfunc = 0;
parsexml(&parser);
}
void
FreePortListing(struct PortMappingParserData * pdata)
{
struct PortMapping * pm;
while((pm = pdata->l_head) != NULL)
{
/* remove from list */
pdata->l_head = pm->l_next;
free(pm);
}
}

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