DEADSOFTWARE

added port forwarding via miniupnpc (disabled by default for now)
authorfgsfds <pvt.fgsfds@gmail.com>
Mon, 5 Feb 2018 16:38:30 +0000 (19:38 +0300)
committerfgsfds <pvt.fgsfds@gmail.com>
Mon, 5 Feb 2018 16:38:30 +0000 (19:38 +0300)
src/game/Doom2DF.lpr
src/game/g_net.pas
src/lib/miniupnpc/miniupnpc.pas [new file with mode: 0644]

index 7cbb675bd6f16d96f2506d1223b31fd31cddf9e2..429c5f0cedc70f4af59855200e987197363db4bc 100644 (file)
@@ -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',
index cbf5120782839a5703caa116a5d0041465411410..93525c990b7913d41175c6f7ee67d724f713ce7a 100644 (file)
@@ -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 (file)
index 0000000..3e453b1
--- /dev/null
@@ -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.