summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSepalani <sepalani@hotmail.fr>2022-08-04 21:36:23 +0400
committerSepalani <sepalani@hotmail.fr>2022-08-28 23:09:09 +0400
commit7dc647fd4cfabdf07746c28d89c8d0c3d6eb3706 (patch)
treef66ade29bf4fd37eff4a098b68e1b3d2ffa7eda6
parente65d56a957e85f55ab16c5f3c8c8f6578f0fe7a4 (diff)
BBA/BuiltIn: Add UPnP HTTP listener
-rw-r--r--Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp105
-rw-r--r--Source/Core/Core/HW/EXI/BBA/BuiltIn.h3
-rw-r--r--Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h2
3 files changed, 103 insertions, 7 deletions
diff --git a/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp b/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp
index 3af54b4f23..d0b169da71 100644
--- a/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp
+++ b/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp
@@ -92,6 +92,9 @@ bool CEXIETHERNET::BuiltInBBAInterface::Activate()
ref.ip = 0;
}
+ m_upnp_httpd.listen(Common::SSDP_PORT, sf::IpAddress(ip));
+ m_upnp_httpd.setBlocking(false);
+
return RecvInit();
}
@@ -116,6 +119,7 @@ void CEXIETHERNET::BuiltInBBAInterface::Deactivate()
}
m_arp_table.clear();
+ m_upnp_httpd.close();
// Wait for read thread to exit.
if (m_read_thread.joinable())
@@ -298,18 +302,30 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(const Common::TCPPacket&
sf::IpAddress target;
StackRef* ref = GetTCPSlot(tcp_header.source_port, tcp_header.destination_port,
Common::BitCast<u32>(ip_header.destination_addr));
- const u16 properties = ntohs(tcp_header.properties);
- if (properties & (TCP_FLAG_FIN | TCP_FLAG_RST))
+ const u16 flags = ntohs(tcp_header.properties) & 0xfff;
+ if (flags & (TCP_FLAG_FIN | TCP_FLAG_RST))
{
if (ref == nullptr)
return; // not found
- ref->ack_num++;
+ ref->ack_num += 1 + static_cast<u32>(data.size());
WriteToQueue(BuildFINFrame(ref));
ref->ip = 0;
+ if (!data.empty())
+ ref->tcp_socket.send(data.data(), data.size());
ref->tcp_socket.disconnect();
}
- else if (properties & TCP_FLAG_SIN)
+ else if (flags == (TCP_FLAG_SIN | TCP_FLAG_ACK))
+ {
+ if (ref == nullptr)
+ return; // not found
+
+ ref->seq_num++;
+ ref->ack_num = ntohl(tcp_header.sequence_number) + 1;
+ ref->ready = true;
+ WriteToQueue(BuildAckFrame(ref));
+ }
+ else if (flags & TCP_FLAG_SIN)
{
// new connection
if (ref != nullptr)
@@ -322,7 +338,7 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(const Common::TCPPacket&
ref->ack_num = ntohl(tcp_header.sequence_number) + 1;
ref->ack_base = ref->ack_num;
ref->seq_num = 0x1000000;
- ref->window_size = ntohl(tcp_header.window_size);
+ ref->window_size = ntohs(tcp_header.window_size);
ref->type = IPPROTO_TCP;
for (auto& tcp_buf : ref->tcp_buffers)
tcp_buf.used = false;
@@ -339,7 +355,10 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(const Common::TCPPacket&
Common::TCPPacket result(ref->bba_mac, ref->my_mac, ref->from, ref->to, ref->seq_num,
ref->ack_num, TCP_FLAG_SIN | TCP_FLAG_ACK);
- result.tcp_options = {0x02, 0x04, 0x05, 0xb4, 0x01, 0x01, 0x01, 0x01};
+ result.tcp_options = {
+ 0x02, 0x04, 0x05, 0xb4, // Maximum segment size: 1460 bytes
+ 0x01, 0x01, 0x01, 0x01 // NOPs
+ };
ref->seq_num++;
target = sf::IpAddress(ntohl(destination_ip));
@@ -488,6 +507,48 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleUDPFrame(const Common::UDPPacket&
ref->udp_socket.send(data.data(), data.size(), target, ntohs(udp_header.destination_port));
}
+void CEXIETHERNET::BuiltInBBAInterface::HandleUPnPClient()
+{
+ StackRef* ref = GetAvailableSlot(0);
+ if (m_upnp_httpd.accept(ref->tcp_socket) != sf::Socket::Done)
+ return;
+
+ if (ref->tcp_socket.GetPeerName(&ref->from) != sf::Socket::Status::Done ||
+ ref->tcp_socket.GetSockName(&ref->to) != sf::Socket::Status::Done)
+ {
+ ERROR_LOG_FMT(SP1, "Failed to accept new UPnP client: {}", Common::StrNetworkError());
+ return;
+ }
+
+ ref->delay = GetTickCountStd();
+ ref->ip = ref->from.sin_addr.s_addr;
+ ref->local = ref->to.sin_port;
+ ref->remote = ref->from.sin_port;
+ ref->ack_num = 0;
+ ref->ack_base = ref->ack_num;
+ ref->seq_num = 0x1000000;
+ ref->window_size = 8192;
+ ref->type = IPPROTO_TCP;
+ for (auto& tcp_buf : ref->tcp_buffers)
+ tcp_buf.used = false;
+ ref->bba_mac = m_current_mac;
+ ref->my_mac = ResolveAddress(ref->from.sin_addr.s_addr);
+ ref->tcp_socket.setBlocking(false);
+ ref->ready = false;
+
+ Common::TCPPacket result(ref->bba_mac, ref->my_mac, ref->from, ref->to, ref->seq_num,
+ ref->ack_num, TCP_FLAG_SIN);
+ // Based on Nintendont packet capture of Mario Kart: Double Dash!!
+ result.tcp_options = {
+ 0x02, 0x04, 0x05, 0xb4, // Maximum segment size: 1460 bytes
+ 0x01, // NOP
+ 0x03, 0x03, 0x08, // Window scale: 8 (multiply by 256)
+ 0x01, 0x01, // NOPs
+ 0x04, 0x02 // SACK permitted
+ };
+ WriteToQueue(result.Build());
+}
+
const Common::MACAddress& CEXIETHERNET::BuiltInBBAInterface::ResolveAddress(u32 inet_ip)
{
auto it = m_arp_table.lower_bound(inet_ip);
@@ -669,6 +730,9 @@ void CEXIETHERNET::BuiltInBBAInterface::ReadThreadHandler(CEXIETHERNET::BuiltInB
}
}
+ // Check for new UPnP client
+ self->HandleUPnPClient();
+
if (datasize > 0)
{
u8* buffer = reinterpret_cast<u8*>(self->m_eth_ref->mRecvBuffer.get());
@@ -678,7 +742,12 @@ void CEXIETHERNET::BuiltInBBAInterface::ReadThreadHandler(CEXIETHERNET::BuiltInB
{
SetIPIdentification(buffer, datasize, ++self->m_ip_frame_id);
}
- self->m_eth_ref->mRecvBufferLength = datasize > 64 ? static_cast<u32>(datasize) : 64;
+ if (datasize < 64)
+ {
+ std::fill(buffer + datasize, buffer + 64, 0);
+ datasize = 64;
+ }
+ self->m_eth_ref->mRecvBufferLength = static_cast<u32>(datasize);
self->m_eth_ref->RecvHandlePacket();
}
}
@@ -727,6 +796,28 @@ sf::Socket::Status BbaTcpSocket::Connect(const sf::IpAddress& dest, u16 port, u3
return this->connect(dest, port);
}
+sf::Socket::Status BbaTcpSocket::GetPeerName(sockaddr_in* addr) const
+{
+ socklen_t size = sizeof(*addr);
+ if (getpeername(getHandle(), reinterpret_cast<sockaddr*>(addr), &size) == -1)
+ {
+ ERROR_LOG_FMT(SP1, "getpeername failed: {}", Common::StrNetworkError());
+ return sf::Socket::Status::Error;
+ }
+ return sf::Socket::Status::Done;
+}
+
+sf::Socket::Status BbaTcpSocket::GetSockName(sockaddr_in* addr) const
+{
+ socklen_t size = sizeof(*addr);
+ if (getsockname(getHandle(), reinterpret_cast<sockaddr*>(addr), &size) == -1)
+ {
+ ERROR_LOG_FMT(SP1, "getsockname failed: {}", Common::StrNetworkError());
+ return sf::Socket::Status::Error;
+ }
+ return sf::Socket::Status::Done;
+}
+
BbaUdpSocket::BbaUdpSocket() = default;
sf::Socket::Status BbaUdpSocket::Bind(u16 port, u32 net_ip)
diff --git a/Source/Core/Core/HW/EXI/BBA/BuiltIn.h b/Source/Core/Core/HW/EXI/BBA/BuiltIn.h
index 35e74eae43..4478f4de48 100644
--- a/Source/Core/Core/HW/EXI/BBA/BuiltIn.h
+++ b/Source/Core/Core/HW/EXI/BBA/BuiltIn.h
@@ -5,6 +5,7 @@
#ifdef _WIN32
#include <WinSock2.h>
+using socklen_t = int;
#else
#include <netinet/in.h>
#endif
@@ -45,6 +46,8 @@ public:
BbaTcpSocket();
sf::Socket::Status Connect(const sf::IpAddress& dest, u16 port, u32 net_ip);
+ sf::Socket::Status GetPeerName(sockaddr_in* addr) const;
+ sf::Socket::Status GetSockName(sockaddr_in* addr) const;
};
class BbaUdpSocket : public sf::UdpSocket
diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h
index 4a6c4019f5..48697f704e 100644
--- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h
+++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h
@@ -449,6 +449,7 @@ private:
u32 m_router_ip = 0;
Common::MACAddress m_router_mac{};
std::map<u32, Common::MACAddress> m_arp_table;
+ sf::TcpListener m_upnp_httpd;
#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__)
std::array<StackRef, 10> network_ref{}; // max 10 at same time, i think most gc game had a
@@ -468,6 +469,7 @@ private:
void HandleTCPFrame(const Common::TCPPacket& packet);
void InitUDPPort(u16 port);
void HandleUDPFrame(const Common::UDPPacket& packet);
+ void HandleUPnPClient();
const Common::MACAddress& ResolveAddress(u32 inet_ip);
};