From 73397e8621bfed506a9ae32e36e8e0d205b220a5 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Mon, 5 Feb 2018 19:38:30 +0300 Subject: [PATCH] added port forwarding via miniupnpc (disabled by default for now) --- src/game/Doom2DF.lpr | 1 + src/game/g_net.pas | 141 ++++++++++++++++++++- src/lib/miniupnpc/miniupnpc.pas | 213 ++++++++++++++++++++++++++++++++ 3 files changed, 353 insertions(+), 2 deletions(-) create mode 100644 src/lib/miniupnpc/miniupnpc.pas diff --git a/src/game/Doom2DF.lpr b/src/game/Doom2DF.lpr index 7cbb675..429c5f0 100644 --- a/src/game/Doom2DF.lpr +++ b/src/game/Doom2DF.lpr @@ -38,6 +38,7 @@ uses math, GL, GLExt, + miniupnpc in '../lib/miniupnpc/miniupnpc.pas', SDL2 in '../lib/sdl2/sdl2.pas', {$IFDEF USE_SDLMIXER} SDL2_mixer in '../lib/sdl2/SDL2_mixer.pas', diff --git a/src/game/g_net.pas b/src/game/g_net.pas index cbf5120..93525c9 100644 --- a/src/game/g_net.pas +++ b/src/game/g_net.pas @@ -19,7 +19,7 @@ unit g_net; interface uses - e_log, e_msg, ENet, Classes, MAPDEF; + e_log, e_msg, ENet, miniupnpc, Classes, MAPDEF; const NET_PROTOCOL_VER = 173; @@ -136,9 +136,15 @@ var NetForcePlayerUpdate: Boolean = False; NetPredictSelf: Boolean = True; - NetGotKeys: Boolean = False; + NetForwardPorts: Boolean = False; NetGotEverything: Boolean = False; + NetGotKeys: Boolean = False; + + NetPortForwarded: Word = 0; + NetPongForwarded: Boolean = False; + NetIGDControl: AnsiString; + NetIGDService: TURLStr; NetDumpFile: TStream; @@ -181,6 +187,9 @@ procedure g_Net_DumpSendBuffer(); procedure g_Net_DumpRecvBuffer(Buf: penet_uint8; Len: LongWord); procedure g_Net_DumpEnd(); +function g_Net_ForwardPorts(ForwardPongPort: Boolean = True): Boolean; +procedure g_Net_UnforwardPorts(); + implementation uses @@ -305,6 +314,8 @@ begin NetMode := NET_NONE; + g_Net_UnforwardPorts(); + if NetDump then g_Net_DumpEnd(); end; @@ -348,6 +359,8 @@ begin NetAddr.host := IPAddr; NetAddr.port := Port; + if NetForwardPorts then g_Net_ForwardPorts(); + NetHost := enet_host_create(@NetAddr, NET_MAXCLIENTS, NET_CHANS, 0, 0); if (NetHost = nil) then @@ -1097,6 +1110,130 @@ begin NetDumpFile := nil; end; +function g_Net_ForwardPorts(ForwardPongPort: Boolean = True): Boolean; +var + DevList: PUPNPDev; + Urls: TUPNPUrls; + Data: TIGDDatas; + LanAddr: array [0..255] of Char; + ExtAddr: array [0..40] of Char; + StrPort: AnsiString; + Err, I: Integer; +begin + Result := False; + + if NetPortForwarded = NetPort then + begin + Result := True; + exit; + end; + + conwriteln('trying to forward server ports...'); + + NetPongForwarded := False; + NetPortForwarded := 0; + + DevList := upnpDiscover(2000, nil, nil, 0, 0, Addr(Err)); + if DevList = nil then + begin + conwritefln(' upnpDiscover() failed: %d', [Err]); + exit; + end; + + I := UPNP_GetValidIGD(DevList, @Urls, @Data, Addr(LanAddr[0]), 256); + + if I = 0 then + begin + conwriteln(' could not find an IGD device on this LAN, aborting'); + FreeUPNPDevList(DevList); + FreeUPNPUrls(@Urls); + exit; + end + else if I = 1 then + conwritefln(' found IGD @ %s', [Urls.controlURL]) + else + conwritefln(' found some kind of UPNP device @ %s, maybe it''ll work', [Urls.controlURL]); + + UPNP_GetExternalIPAddress(Urls.controlURL, Addr(data.first.servicetype[1]), Addr(ExtAddr[0])); + if ExtAddr[0] <> #0 then + conwritefln(' external IP address: %s', [Addr(ExtAddr[0])]); + + StrPort := IntToStr(NetPort); + I := UPNP_AddPortMapping( + Urls.controlURL, Addr(data.first.servicetype[1]), + PChar(StrPort), PChar(StrPort), Addr(LanAddr[0]), PChar('D2DF'), + PChar('UDP'), nil, PChar('0') + ); + + if I <> 0 then + begin + conwritefln(' forwarding port %d failed: error %d', [NetPort, I]); + FreeUPNPDevList(DevList); + FreeUPNPUrls(@Urls); + exit; + end; + + if ForwardPongPort then + begin + StrPort := IntToStr(NetPort + 1); + I := UPNP_AddPortMapping( + Urls.controlURL, Addr(data.first.servicetype[1]), + PChar(StrPort), PChar(StrPort), Addr(LanAddr[0]), PChar('D2DF'), + PChar('UDP'), nil, PChar('0') + ); + + if I <> 0 then + begin + conwritefln(' forwarding port %d failed: error %d', [NetPort + 1, I]); + NetPongForwarded := False; + end + else + begin + conwritefln(' forwarded port %d successfully', [NetPort + 1]); + NetPongForwarded := True; + end; + end; + + conwritefln(' forwarded port %d successfully', [NetPort]); + NetIGDControl := AnsiString(Urls.controlURL); + NetIGDService := data.first.servicetype; + NetPortForwarded := NetPort; + + FreeUPNPDevList(DevList); + FreeUPNPUrls(@Urls); + Result := True; +end; + +procedure g_Net_UnforwardPorts(); +var + I: Integer; + StrPort: AnsiString; +begin + if NetPortForwarded = 0 then Exit; + + conwriteln('unforwarding ports...'); + + StrPort := IntToStr(NetPortForwarded); + I := UPNP_DeletePortMapping( + PChar(NetIGDControl), Addr(NetIGDService[1]), + PChar(StrPort), PChar('UDP'), nil + ); + conwritefln(' port %d: %d', [NetPortForwarded, I]); + + if NetPongForwarded then + begin + NetPongForwarded := False; + StrPort := IntToStr(NetPortForwarded + 1); + I := UPNP_DeletePortMapping( + PChar(NetIGDControl), Addr(NetIGDService[1]), + PChar(StrPort), PChar('UDP'), nil + ); + conwritefln(' port %d: %d', [NetPortForwarded + 1, I]); + end; + + NetPortForwarded := 0; +end; + initialization NetIn.Alloc(NET_BUFSIZE); diff --git a/src/lib/miniupnpc/miniupnpc.pas b/src/lib/miniupnpc/miniupnpc.pas new file mode 100644 index 0000000..3e453b1 --- /dev/null +++ b/src/lib/miniupnpc/miniupnpc.pas @@ -0,0 +1,213 @@ +{$DEFINE LIBMINIUPNPC_WINDOZE_STATIC} + +{$MODE OBJFPC} +{$PACKRECORDS C} + +{$IFDEF WIN32} + {$DEFINE MSWINDOWS} +{$ENDIF} + +{$LONGSTRINGS ON} +{$MACRO ON} + +{$Z4} // Force four-byte enums + +unit miniupnpc; + +interface + +{$IFDEF MSWINDOWS} + {$IFDEF LIBMINIUPNPC_WINDOZE_STATIC} + {$LINKLIB libminiupnpc.a} + {$LINKLIB libiphlpapi.a} + {$DEFINE MINIUPNPC_IMPL := cdecl; external} + {$ELSE} + {$DEFINE MINIUPNPC_IMPL := cdecl; external 'miniupnpc.dll'} + {$ENDIF} +{$ELSE} + {$DEFINE MINIUPNPC_IMPL := cdecl; external 'miniupnpc'} +{$ENDIF} + +const MINIUPNPC_URL_MAXSIZE=128; +Type + PUPNPDev = ^TUPNPDev; + TUPNPDev = record + pNext:PUPNPDev; + descURL:pchar; + st:pchar; + scope_id:word; + buffer:array[0..1] of byte; + end; + + PUPNPUrls = ^TUPNPUrls; + TUPNPUrls = record + controlURL:pchar; + ipcondescURL:pchar; + controlURL_CIF:pchar; + controlURL_6FC:pchar; + rootdescURL:pchar; + end; + TUrlStr = array [1..MINIUPNPC_URL_MAXSIZE] of char; + TIGDdatas_service = record + controlurl : TUrlStr; + eventsuburl: TUrlStr; + scpdurl: TUrlStr; + servicetype: TUrlStr; + //char devicetype[MINIUPNPC_URL_MAXSIZE]; + end; + + PIGDdatas = ^TIGDdatas; + TIGDdatas = record + cureltname: TUrlStr; + urlbase: TUrlStr; + presentationurl : TUrlStr; + level:integer; + //int state; + //"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" + CIF : TIGDdatas_service; + // "urn:schemas-upnp-org:service:WANIPConnection:1" + // "urn:schemas-upnp-org:service:WANPPPConnection:1" + first: TIGDdatas_service; + //if both WANIPConnection and WANPPPConnection are present + second: TIGDdatas_service; + //"urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" + IPv6FC : TIGDdatas_service; + // tmp + tmp: TIGDdatas_service; + end; + +(* upnpDiscover() + * discover UPnP devices on the network. + * The discovered devices are returned as a chained list. + * It is up to the caller to free the list with freeUPNPDevlist(). + * 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 sameport is not null, SSDP packets will be sent from the source port + * 1900 (same as destination port) otherwise system assign a source port. *) +function upnpDiscover( + delay:integer; + multicastif:pchar; + minissdpdsock:pchar; + sameport:integer; + IPV6:integer; + error:pinteger):PUPNPDev; MINIUPNPC_IMPL; + + +(* UPNP_GetValidIGD() : + * return values : + * 0 = NO IGD found + * 1 = A valid connected IGD has been found + * 2 = A valid IGD has been found but it reported as + * not connected + * 3 = an UPnP device has been found but was not recognized as an IGD + * + * 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. + *) +function UPNP_GetValidIGD( + devlist:PUPNPDev; + urls:PUPNPUrls; + data:PIGDdatas; + lanaddr:pchar; + lanaddrlen:integer):integer; MINIUPNPC_IMPL; + + +(* UPNP_GetExternalIPAddress() call the corresponding UPNP method. + * if the third arg is not null the value is copied to it. + * at least 16 bytes must be available + * + * Return values : + * 0 : SUCCESS + * NON ZERO : ERROR Either an UPnP error code or an unknown error. + * + * possible UPnP Errors : + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 501 Action Failed - See UPnP Device Architecture section on Control. *) +function UPNP_GetExternalIPAddress( + controlURL:pchar; + servicetype:pchar; + extIpAdd:pchar):integer; MINIUPNPC_IMPL; + + +(* UPNP_AddPortMapping() + * if desc is NULL, it will be defaulted to "libminiupnpc" + * remoteHost is usually NULL because IGD don't support it. + * + * Return values : + * 0 : SUCCESS + * NON ZERO : ERROR. Either an UPnP error code or an unknown error. + * + * List of possible UPnP errors for AddPortMapping : + * errorCode errorDescription (short) - Description (long) + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 501 Action Failed - See UPnP Device Architecture section on Control. + * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be + * wild-carded + * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded + * 718 ConflictInMappingEntry - The port mapping entry specified conflicts + * with a mapping assigned previously to another client + * 724 SamePortValuesRequired - Internal and External port values + * must be the same + * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports + * permanent lease times on port mappings + * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard + * and cannot be a specific IP address or DNS name + * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and + * cannot be a specific port value *) + +function UPNP_AddPortMapping( + controlURL:pchar; + servicetype:pchar; + extPort:pchar; + inPort:pchar; + inClient:pchar; + desc:pchar; + proto:pchar; + remoteHost:pchar; + leaseDuration:pchar):integer; MINIUPNPC_IMPL; + +(* UPNP_DeletePortMapping() + * Use same argument values as what was used for AddPortMapping(). + * remoteHost is usually NULL because IGD don't support it. + * Return Values : + * 0 : SUCCESS + * NON ZERO : error. Either an UPnP error code or an undefined error. + * + * List of possible UPnP errors for DeletePortMapping : + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 714 NoSuchEntryInArray - The specified value does not exist in the array *) + +function UPNP_DeletePortMapping( + controlURL:pchar; + servicetype:pchar; + extPort:pchar; + proto:pchar; + remoteHost:pchar):integer; MINIUPNPC_IMPL; + + + +function UPNP_GetGenericPortMappingEntry( + const controlURL :pchar; + const servicetype:pchar; + index:pchar; + extPort:pchar; + intClient:pchar; + intPort:pchar; + protocol:pchar; + desc:pchar; + enabled:pchar; + rHost:pchar; + duration:pchar):integer; MINIUPNPC_IMPL; + +procedure FreeUPNPUrls(urls: PUPNPUrls); MINIUPNPC_IMPL; +procedure freeUPNPDevlist(devl: PUPNPDev); MINIUPNPC_IMPL; + +implementation + +end. -- 2.29.2