BBA/BuiltIn: Add UPnP HTTP listener
This commit is contained in:
parent
e65d56a957
commit
7dc647fd4c
|
@ -92,6 +92,9 @@ bool CEXIETHERNET::BuiltInBBAInterface::Activate()
|
||||||
ref.ip = 0;
|
ref.ip = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_upnp_httpd.listen(Common::SSDP_PORT, sf::IpAddress(ip));
|
||||||
|
m_upnp_httpd.setBlocking(false);
|
||||||
|
|
||||||
return RecvInit();
|
return RecvInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +119,7 @@ void CEXIETHERNET::BuiltInBBAInterface::Deactivate()
|
||||||
}
|
}
|
||||||
|
|
||||||
m_arp_table.clear();
|
m_arp_table.clear();
|
||||||
|
m_upnp_httpd.close();
|
||||||
|
|
||||||
// Wait for read thread to exit.
|
// Wait for read thread to exit.
|
||||||
if (m_read_thread.joinable())
|
if (m_read_thread.joinable())
|
||||||
|
@ -298,18 +302,30 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(const Common::TCPPacket&
|
||||||
sf::IpAddress target;
|
sf::IpAddress target;
|
||||||
StackRef* ref = GetTCPSlot(tcp_header.source_port, tcp_header.destination_port,
|
StackRef* ref = GetTCPSlot(tcp_header.source_port, tcp_header.destination_port,
|
||||||
Common::BitCast<u32>(ip_header.destination_addr));
|
Common::BitCast<u32>(ip_header.destination_addr));
|
||||||
const u16 properties = ntohs(tcp_header.properties);
|
const u16 flags = ntohs(tcp_header.properties) & 0xfff;
|
||||||
if (properties & (TCP_FLAG_FIN | TCP_FLAG_RST))
|
if (flags & (TCP_FLAG_FIN | TCP_FLAG_RST))
|
||||||
{
|
{
|
||||||
if (ref == nullptr)
|
if (ref == nullptr)
|
||||||
return; // not found
|
return; // not found
|
||||||
|
|
||||||
ref->ack_num++;
|
ref->ack_num += 1 + static_cast<u32>(data.size());
|
||||||
WriteToQueue(BuildFINFrame(ref));
|
WriteToQueue(BuildFINFrame(ref));
|
||||||
ref->ip = 0;
|
ref->ip = 0;
|
||||||
|
if (!data.empty())
|
||||||
|
ref->tcp_socket.send(data.data(), data.size());
|
||||||
ref->tcp_socket.disconnect();
|
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
|
// new connection
|
||||||
if (ref != nullptr)
|
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_num = ntohl(tcp_header.sequence_number) + 1;
|
||||||
ref->ack_base = ref->ack_num;
|
ref->ack_base = ref->ack_num;
|
||||||
ref->seq_num = 0x1000000;
|
ref->seq_num = 0x1000000;
|
||||||
ref->window_size = ntohl(tcp_header.window_size);
|
ref->window_size = ntohs(tcp_header.window_size);
|
||||||
ref->type = IPPROTO_TCP;
|
ref->type = IPPROTO_TCP;
|
||||||
for (auto& tcp_buf : ref->tcp_buffers)
|
for (auto& tcp_buf : ref->tcp_buffers)
|
||||||
tcp_buf.used = false;
|
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,
|
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);
|
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++;
|
ref->seq_num++;
|
||||||
target = sf::IpAddress(ntohl(destination_ip));
|
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));
|
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)
|
const Common::MACAddress& CEXIETHERNET::BuiltInBBAInterface::ResolveAddress(u32 inet_ip)
|
||||||
{
|
{
|
||||||
auto it = m_arp_table.lower_bound(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)
|
if (datasize > 0)
|
||||||
{
|
{
|
||||||
u8* buffer = reinterpret_cast<u8*>(self->m_eth_ref->mRecvBuffer.get());
|
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);
|
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();
|
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);
|
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;
|
BbaUdpSocket::BbaUdpSocket() = default;
|
||||||
|
|
||||||
sf::Socket::Status BbaUdpSocket::Bind(u16 port, u32 net_ip)
|
sf::Socket::Status BbaUdpSocket::Bind(u16 port, u32 net_ip)
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <WinSock2.h>
|
#include <WinSock2.h>
|
||||||
|
using socklen_t = int;
|
||||||
#else
|
#else
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -45,6 +46,8 @@ public:
|
||||||
BbaTcpSocket();
|
BbaTcpSocket();
|
||||||
|
|
||||||
sf::Socket::Status Connect(const sf::IpAddress& dest, u16 port, u32 net_ip);
|
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
|
class BbaUdpSocket : public sf::UdpSocket
|
||||||
|
|
|
@ -449,6 +449,7 @@ private:
|
||||||
u32 m_router_ip = 0;
|
u32 m_router_ip = 0;
|
||||||
Common::MACAddress m_router_mac{};
|
Common::MACAddress m_router_mac{};
|
||||||
std::map<u32, Common::MACAddress> m_arp_table;
|
std::map<u32, Common::MACAddress> m_arp_table;
|
||||||
|
sf::TcpListener m_upnp_httpd;
|
||||||
#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
|
#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
|
||||||
defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__)
|
defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__)
|
||||||
std::array<StackRef, 10> network_ref{}; // max 10 at same time, i think most gc game had a
|
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 HandleTCPFrame(const Common::TCPPacket& packet);
|
||||||
void InitUDPPort(u16 port);
|
void InitUDPPort(u16 port);
|
||||||
void HandleUDPFrame(const Common::UDPPacket& packet);
|
void HandleUDPFrame(const Common::UDPPacket& packet);
|
||||||
|
void HandleUPnPClient();
|
||||||
const Common::MACAddress& ResolveAddress(u32 inet_ip);
|
const Common::MACAddress& ResolveAddress(u32 inet_ip);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue