Added a UPnP option to use original port for external port instead of using the shifted port by port offset, to be compatible with real PSP or other PSP emulators

This commit is contained in:
ANR2ME 2020-03-19 15:21:00 +07:00 committed by Henrik Rydgård
parent eea2b23452
commit b9025b50bd
10 changed files with 86 additions and 45 deletions

View File

@ -957,8 +957,9 @@ static ConfigSetting networkSettings[] = {
ConfigSetting("EnableAdhocServer", &g_Config.bEnableAdhocServer, false, true, true),
ConfigSetting("proAdhocServer", &g_Config.proAdhocServer, "myneighborsushicat.com", true, true),
ConfigSetting("PortOffset", &g_Config.iPortOffset, 0, true, true),
ConfigSetting("EnableUPnP", &g_Config.bEnableUPnP, false, true, true),
ConfigSetting("MinTimeout", &g_Config.iMinTimeout, 1, true, true),
ConfigSetting("EnableUPnP", &g_Config.bEnableUPnP, false, true, true),
ConfigSetting("UPnPUseOriginalPort", &g_Config.bUPnPUseOriginalPort, true, true, true),
ConfigSetting("EnableNetworkChat", &g_Config.bEnableNetworkChat, false, true, true),
ConfigSetting("ChatButtonPosition",&g_Config.iChatButtonPosition,BOTTOM_LEFT,true,true),

View File

@ -412,6 +412,7 @@ public:
bool bEnableWlan;
bool bEnableAdhocServer;
bool bEnableUPnP;
bool bUPnPUseOriginalPort;
int iPortOffset;
int iMinTimeout;
int iWlanAdhocChannel;

View File

@ -73,6 +73,7 @@ std::string message = "";
bool chatScreenVisible = false;
bool updateChatScreen = false;
int newChat = 0;
bool isOriPort = false;
bool isLocalServer = false;
sockaddr LocalhostIP;
sockaddr LocalIP;
@ -82,11 +83,7 @@ bool isLocalMAC(const SceNetEtherAddr * addr) {
SceNetEtherAddr saddr;
getLocalMac(&saddr);
// Compare MAC Addresses
int match = memcmp((const void *)addr, (const void *)&saddr, ETHER_ADDR_LEN);
// Return Result
return (match == 0);
return (memcmp((const void*)addr, (const void*)&saddr, ETHER_ADDR_LEN) == 0);
}
bool isPDPPortInUse(uint16_t port) {
@ -1604,6 +1601,33 @@ uint32_t getLocalIp(int sock) {
return localAddr.sin_addr.s_addr;
}
static std::vector<std::pair<uint32_t, uint32_t>> InitPrivateIPRanges() {
struct sockaddr_in saNet, saMask;
std::vector<std::pair<uint32_t, uint32_t>> ip_ranges;
if (1 == inet_pton(AF_INET, "192.168.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.255.0.0", &(saMask.sin_addr)))
ip_ranges.push_back({saNet.sin_addr.s_addr, saMask.sin_addr.s_addr});
if (1 == inet_pton(AF_INET, "172.16.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.240.0.0", &(saMask.sin_addr)))
ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });
if (1 == inet_pton(AF_INET, "10.0.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.0.0.0", &(saMask.sin_addr)))
ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });
if (1 == inet_pton(AF_INET, "127.0.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.0.0.0", &(saMask.sin_addr)))
ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });
if (1 == inet_pton(AF_INET, "169.254.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.255.0.0", &(saMask.sin_addr)))
ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });
return ip_ranges;
}
bool isPrivateIP(uint32_t ip) {
static const std::vector<std::pair<uint32_t, uint32_t>> ip_ranges = InitPrivateIPRanges();
for (auto ipRange : ip_ranges) {
if ((ip & ipRange.second) == (ipRange.first & ipRange.second)) // We can just use ipRange.first directly if it's already correctly formatted
return true;
}
return false;
}
void getLocalMac(SceNetEtherAddr * addr){
// Read MAC Address from config
uint8_t mac[ETHER_ADDR_LEN] = {0};
@ -1859,10 +1883,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){
}
bool isBroadcastMAC(const SceNetEtherAddr * addr) {
// Broadcast MAC
if (memcmp(addr->data, "\xFF\xFF\xFF\xFF\xFF\xFF", ETHER_ADDR_LEN) == 0) return true;
// Normal MAC
return false;
return (memcmp(addr->data, "\xFF\xFF\xFF\xFF\xFF\xFF", ETHER_ADDR_LEN) == 0);
}
bool resolveIP(uint32_t ip, SceNetEtherAddr * mac) {

View File

@ -844,6 +844,7 @@ extern SceNetAdhocPtpStat * ptp[255];
extern uint16_t portOffset;
extern uint32_t minSocketTimeoutUS;
extern bool isOriPort;
extern bool isLocalServer;
extern sockaddr LocalhostIP; // Used to differentiate localhost IP on multiple-instance
extern sockaddr LocalIP; // IP of Network Adapter used to connect to Adhoc Server (LAN/WAN)
@ -1159,6 +1160,11 @@ int getActivePeerCount(const bool excludeTimedout = true);
int getLocalIp(sockaddr_in * SocketAddress);
uint32_t getLocalIp(int sock);
/*
* Check if an IP (big-endian/network order) is Private or Public IP
*/
bool isPrivateIP(uint32_t ip);
/*
* Get UDP Socket Max Message Size
*/

View File

@ -1685,13 +1685,13 @@ int proAdhocServerThread(int port) // (int argc, char * argv[])
INFO_LOG(SCENET, "AdhocServer: Listening for Connections on TCP Port %u", port); //SERVER_PORT
// Port forward
g_PortManager.Add(port, IP_PROTOCOL_TCP);
g_PortManager.Add(IP_PROTOCOL_TCP, port);
// Enter Server Loop
result = server_loop(server);
// Remove Port mapping
g_PortManager.Remove(port, IP_PROTOCOL_TCP);
g_PortManager.Remove(IP_PROTOCOL_TCP, port);
// Notify User
INFO_LOG(SCENET, "AdhocServer: Shutdown complete");

View File

@ -118,6 +118,7 @@ static void __ResetInitNetLib() {
void __NetInit() {
// Windows: Assuming WSAStartup already called beforehand
portOffset = g_Config.iPortOffset;
isOriPort = g_Config.bEnableUPnP && g_Config.bUPnPUseOriginalPort;
minSocketTimeoutUS = g_Config.iMinTimeout * 1000UL;
InitLocalhostIP();

View File

@ -377,7 +377,7 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 u
// Fill in Data
internal->id = usocket;
internal->laddr = *saddr;
internal->lport = port; //getLocalPort(usocket) - portOffset; //should use the port given to the socket (in case it's UNUSED_PORT port) isn't?
internal->lport = port; //getLocalPort(usocket) - portOffset;
internal->rcv_sb_cc = bufferSize;
// Link Socket to Translator ID
@ -385,7 +385,7 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 u
// Forward Port on Router
//sceNetPortOpen("UDP", port);
g_PortManager.Add(port + portOffset, IP_PROTOCOL_UDP);
g_PortManager.Add(IP_PROTOCOL_UDP, isOriPort ? port : port + portOffset, port + portOffset);
// Success
return i + 1;
@ -519,6 +519,9 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int
// Get Peer IP
if (resolveMAC((SceNetEtherAddr *)daddr, (uint32_t *)&target.sin_addr.s_addr)) {
// Some games (ie. PSP2) might try to talk to it's self, not sure if they talked through WAN or LAN when using public Adhoc Server tho
target.sin_port = htons(dport + ((isOriPort && !isPrivateIP(target.sin_addr.s_addr)) ? 0 : portOffset));
// Acquire Network Lock
//_acquireNetworkLock();
@ -588,7 +591,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int
sockaddr_in target;
target.sin_family = AF_INET;
target.sin_addr.s_addr = peer->ip_addr;
target.sin_port = htons(dport + portOffset);
target.sin_port = htons(dport + ((isOriPort && !isPrivateIP(peer->ip_addr)) ? 0 : portOffset));
int sent = sendto(socket->id, (const char *)data, len, 0, (sockaddr *)&target, sizeof(target));
int error = errno;
@ -980,7 +983,7 @@ static int sceNetAdhocPdpDelete(int id, int unknown) {
// Remove Port Forward from Router
//sceNetPortClose("UDP", sock->lport);
//g_PortManager.Remove(sock->lport + portOffset, IP_PROTOCOL_UDP); // Let's not remove mapping in real-time as it could cause lags/disconnection when joining a room with slow routers
//g_PortManager.Remove(IP_PROTOCOL_UDP, isOriPort ? sock->lport : sock->lport + portOffset); // Let's not remove mapping in real-time as it could cause lags/disconnection when joining a room with slow routers
// Free Memory
free(sock);
@ -1870,6 +1873,7 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac,
}
SceNetEtherAddr* saddr = (SceNetEtherAddr*)srcmac;
SceNetEtherAddr* daddr = (SceNetEtherAddr*)dstmac;
bool isClient = false;
// Library is initialized
if (netAdhocInited) {
// Some games (ie. DBZ Shin Budokai 2) might be getting the saddr/srcmac content from SaveState and causing problems if current MAC is different :( So we try to fix it here
@ -1880,6 +1884,7 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac,
if (saddr != NULL && isLocalMAC(saddr) && daddr != NULL && !isBroadcastMAC(daddr)) {
// Random Port required
if (sport == 0) {
isClient = true;
// Find unused Port
// while (sport == 0 || _IsPTPPortInUse(sport)) {
// // Generate Port Number
@ -1963,7 +1968,8 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac,
// Add Port Forward to Router. We may not even need to forward this local port, since PtpOpen usually have port 0 (any port) as source port and followed by PtpConnect (which mean acting as Client), right?
//sceNetPortOpen("TCP", sport);
//g_PortManager.Add(sport + portOffset, IP_PROTOCOL_TCP);
if (!isClient)
g_PortManager.Add(IP_PROTOCOL_TCP, isOriPort ? sport : sport + portOffset, sport + portOffset);
// Return PTP Socket Pointer
return i + 1;
@ -2153,7 +2159,7 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int
// Add Port Forward to Router. Or may be doesn't need to be forwarded since local port already accessible from outside if others were able to connect & get accepted at this point, right?
//sceNetPortOpen("TCP", internal->lport);
//g_PortManager.Add(internal->lport + portOffset, IP_PROTOCOL_TCP);
//g_PortManager.Add(IP_PROTOCOL_TCP, internal->lport + portOffset);
INFO_LOG(SCENET, "sceNetAdhocPtpAccept[%i->%i:%u]: Established (%s:%u)", id, i+1, internal->lport, inet_ntoa(peeraddr.sin_addr), internal->pport);
@ -2234,6 +2240,9 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) {
// Grab Peer IP
if (resolveMAC(&socket->paddr, (uint32_t *)&sin.sin_addr.s_addr)) {
// Some games (ie. PSP2) might try to talk to it's self, not sure if they talked through WAN or LAN when using public Adhoc Server tho
sin.sin_port = htons(socket->pport + ((isOriPort && !isPrivateIP(sin.sin_addr.s_addr)) ? 0 : portOffset));
// Grab Nonblocking Flag
uint32_t nbio = getBlockingFlag(socket->id);
@ -2245,7 +2254,7 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) {
}*/
// Connect Socket to Peer (Nonblocking)
// Fixme: The First Non-blocking POSIX connect will always returns EAGAIN/EWOULDBLOCK because it returns without waiting for ACK, But GvG Next Plus is treating non-blocking connect just like blocking connect, May be on a real PSP the first non-blocking sceNetAdhocPtpConnect can be successfull?
// NOTE: Based on what i read at stackoverflow, The First Non-blocking POSIX connect will always returns EAGAIN/EWOULDBLOCK because it returns without waiting for ACK/handshake, But GvG Next Plus is treating non-blocking PtpConnect just like blocking connect, May be on a real PSP the first non-blocking sceNetAdhocPtpConnect can be successfull?
int connectresult = connect(socket->id, (sockaddr *)&sin, sizeof(sin));
// Grab Error Code
@ -2352,7 +2361,7 @@ static int sceNetAdhocPtpClose(int id, int unknown) {
// Remove Port Forward from Router
//sceNetPortClose("TCP", socket->lport);
//g_PortManager.Remove(socket->lport + portOffset, IP_PROTOCOL_TCP); // Let's not remove mapping in real-time as it could cause lags/disconnection when joining a room with slow routers
//g_PortManager.Remove(IP_PROTOCOL_TCP, isOriPort ? socket->lport : socket->lport + portOffset); // Let's not remove mapping in real-time as it could cause lags/disconnection when joining a room with slow routers
// Free Memory
free(socket);
@ -2488,7 +2497,7 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int
// Add Port Forward to Router
//sceNetPortOpen("TCP", sport);
g_PortManager.Add(sport + portOffset, IP_PROTOCOL_TCP);
g_PortManager.Add(IP_PROTOCOL_TCP, isOriPort ? sport : sport + portOffset, sport + portOffset);
// Return PTP Socket Pointer
return i + 1;

View File

@ -30,8 +30,8 @@
#include <Core/System.h>
#include <Core/Host.h>
#include <Core/ELF/ParamSFO.h>
#include "Core/HLE/proAdhoc.h" // This import is only used to get product id that was used to connect to adhoc server
#include "Core/Util/PortManager.h"
#include <Common/Log.h>
#include "i18n/i18n.h"
@ -77,7 +77,7 @@ bool PortManager::Init(const unsigned int timeout) {
char* descXML;
int descXMLsize = 0;
int descXMLstatus = 0;
int localport = m_LocalPort; // UPNP_LOCAL_PORT_ANY (0), or UPNP_LOCAL_PORT_SAME (1) as an alias for 1900 for backwards compatability?
int localport = m_LocalPort; // UPNP_LOCAL_PORT_ANY (0), or UPNP_LOCAL_PORT_SAME (1) as an alias for 1900 (for backwards compatability?)
int ipv6 = 0; // 0 = IPv4, 1 = IPv6
unsigned char ttl = 2; // defaulting to 2
int error = 0;
@ -151,13 +151,8 @@ bool PortManager::Init(const unsigned int timeout) {
INFO_LOG(SCENET, "PortManager - Connection Type: %s", connectionType);
}
// Using Game ID & Player Name as default description for mapping (prioritizing the ID sent by the game to Adhoc server)
char productid[10] = { 0 };
memcpy(productid, product_code.data, sizeof(product_code.data));
std::string gameID = std::string(productid);
if (productid[0] == '\0') {
gameID = g_paramSFO.GetDiscID();
}
// Using Game ID & Player Name as default description for mapping
std::string gameID = g_paramSFO.GetDiscID();
m_defaultDesc = "PPSSPP:" + gameID + ":" + g_Config.sNickName;
freeUPNPDevlist(devlist);
@ -179,17 +174,21 @@ int PortManager::GetInitState() {
return m_InitState;
}
bool PortManager::Add(unsigned short port, const char* protocol) {
bool PortManager::Add(const char* protocol, unsigned short port, unsigned short intport) {
char port_str[16];
char intport_str[16];
int r;
INFO_LOG(SCENET, "PortManager::Add(%d, %s)", port, protocol);
if (intport == 0)
intport = port;
INFO_LOG(SCENET, "PortManager::Add(%s, %d, %d)", protocol, port, intport);
if (urls == NULL || urls->controlURL == NULL || urls->controlURL[0] == '\0')
{
if (g_Config.bEnableUPnP) WARN_LOG(SCENET, "PortManager::Add - the init was not done !");
return false;
}
sprintf(port_str, "%d", port);
sprintf(intport_str, "%d", intport);
// Only add new port map if it's not previously created by PPSSPP for current IP
auto el_it = std::find_if(m_portList.begin(), m_portList.end(),
[port_str, protocol](const std::pair<std::string, std::string> &el) { return el.first == port_str && el.second == protocol; });
@ -201,11 +200,11 @@ bool PortManager::Add(unsigned short port, const char* protocol) {
r = UPNP_DeletePortMapping(urls->controlURL, datas->first.servicetype, port_str, protocol, NULL);
}
r = UPNP_AddPortMapping(urls->controlURL, datas->first.servicetype,
port_str, port_str, m_lanip.c_str(), m_defaultDesc.c_str(), protocol, NULL, m_leaseDuration.c_str());
port_str, intport_str, m_lanip.c_str(), m_defaultDesc.c_str(), protocol, NULL, m_leaseDuration.c_str());
if (r == 725 && m_leaseDuration != "0") {
m_leaseDuration = "0";
r = UPNP_AddPortMapping(urls->controlURL, datas->first.servicetype,
port_str, port_str, m_lanip.c_str(), m_defaultDesc.c_str(), protocol, NULL, m_leaseDuration.c_str());
port_str, intport_str, m_lanip.c_str(), m_defaultDesc.c_str(), protocol, NULL, m_leaseDuration.c_str());
}
if (r != 0)
{
@ -224,10 +223,10 @@ bool PortManager::Add(unsigned short port, const char* protocol) {
return true;
}
bool PortManager::Remove(unsigned short port, const char* protocol) {
bool PortManager::Remove(const char* protocol, unsigned short port) {
char port_str[16];
INFO_LOG(SCENET, "PortManager::Remove(%d, %s)", port, protocol);
INFO_LOG(SCENET, "PortManager::Remove(%s, %d)", protocol, port);
if (urls == NULL || urls->controlURL == NULL || urls->controlURL[0] == '\0')
{
if (g_Config.bEnableUPnP) WARN_LOG(SCENET, "PortManager::Remove - the init was not done !");

View File

@ -69,19 +69,19 @@ public:
// Get UPnP Initialization status
int GetInitState();
// Add a port & protocol (TCP, UDP or vendor-defined) to map for forwarding
bool Add(unsigned short port, const char* protocol);
// Add a port & protocol (TCP, UDP or vendor-defined) to map for forwarding (intport = 0 : same as [external] port)
bool Add(const char* protocol, unsigned short port, unsigned short intport = 0);
// Remove a port mapping
bool Remove(unsigned short port, const char* protocol);
// Remove a port mapping (external port)
bool Remove(const char* protocol, unsigned short port);
// Removes any lingering mapped ports from previous crashes
// Removes any lingering mapped ports created by PPSSPP from previous crashes
bool Clear();
// Restore ports mapped by others that were taken by PPSSPP, better used after Clear()
bool Restore();
// Get port list mapped by PPSSPP for current LAN IP & others
// Get port lists mapped by PPSSPP for current LAN IP & other's applications
bool RefreshPortList();
protected:

View File

@ -690,7 +690,6 @@ void GameSettingsScreen::CreateViews() {
networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.proAdhocServer, n->T("Change proAdhocServer Address"), (const char *)nullptr))->OnClick.Handle(this, &GameSettingsScreen::OnChangeproAdhocServerAddress);
networkingSettings->Add(new CheckBox(&g_Config.bEnableAdhocServer, n->T("Enable built-in PRO Adhoc Server", "Enable built-in PRO Adhoc Server")));
networkingSettings->Add(new CheckBox(&g_Config.bEnableUPnP, n->T("Enable UPnP", "Enable UPnP (need a few seconds to detect)")));
networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sMACAddress, n->T("Change Mac Address"), (const char *)nullptr))->OnClick.Handle(this, &GameSettingsScreen::OnChangeMacAddress);
static const char* wlanChannels[] = { "Auto", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11" };
auto wlanChannelChoice = networkingSettings->Add(new PopupMultiChoice(&g_Config.iWlanAdhocChannel, gr->T("WLAN Channel"), wlanChannels, 0, ARRAY_SIZE(wlanChannels), gr->GetName(), screenManager()));
@ -698,8 +697,12 @@ void GameSettingsScreen::CreateViews() {
wlanChannelChoice->HideChoice(i+2);
wlanChannelChoice->HideChoice(i+7);
}
networkingSettings->Add(new PopupSliderChoice(&g_Config.iPortOffset, 0, 60000, n->T("Port offset", "Port offset(0 = PSP compatibility)"), 100, screenManager()));
networkingSettings->Add(new PopupSliderChoice(&g_Config.iMinTimeout, 1, 15000, n->T("Minimum Timeout", "Minimum Timeout (override low latency communication in milliseconds)"), 100, screenManager()));
networkingSettings->Add(new PopupSliderChoice(&g_Config.iPortOffset, 0, 60000, n->T("Port offset", "Port offset (0 = PSP compatibility)"), 100, screenManager()));
networkingSettings->Add(new PopupSliderChoice(&g_Config.iMinTimeout, 1, 15000, n->T("Minimum Timeout", "Minimum Timeout (override low latency in ms)"), 100, screenManager()));
networkingSettings->Add(new ItemHeader(n->T("UPnP")));
networkingSettings->Add(new CheckBox(&g_Config.bEnableUPnP, n->T("Enable UPnP", "Enable UPnP (need a few seconds to detect)")));
networkingSettings->Add(new CheckBox(&g_Config.bUPnPUseOriginalPort, n->T("UPnP use original port", "UPnP use original port (PSP compatibility)")))->SetEnabledPtr(&g_Config.bEnableUPnP);
networkingSettings->Add(new ItemHeader(n->T("Chat")));
networkingSettings->Add(new CheckBox(&g_Config.bEnableNetworkChat, n->T("Enable network chat", "Enable network chat")));