picotcp: use random local UDP port if already in use

Bind to random local UDP port if requested one is in use.
Log to NETWORK instead of MODEM.
Fix get_last_error name conflict with asio.
This commit is contained in:
Flyinghead 2025-06-22 11:22:06 +02:00
parent 109e084e18
commit 09247b4503
2 changed files with 32 additions and 21 deletions

View File

@ -53,7 +53,7 @@ typedef int sock_t;
#define L_EWOULDBLOCK EWOULDBLOCK
#define L_EAGAIN EAGAIN
#define L_EINPROGRESS EINPROGRESS
#define get_last_error() (errno)
static inline int get_last_error() { return errno; }
#define INVALID_SOCKET (-1)
#define perror(s) do { ERROR_LOG(NETWORK, "%s: %s", (s) != NULL ? (s) : "", strerror(get_last_error())); } while (false)
#else
@ -62,7 +62,7 @@ typedef SOCKET sock_t;
#define L_EWOULDBLOCK WSAEWOULDBLOCK
#define L_EAGAIN WSAEWOULDBLOCK
#define L_EINPROGRESS WSAEINPROGRESS
#define get_last_error() (WSAGetLastError())
static inline int get_last_error() { return WSAGetLastError(); }
#define perror(s) do { ERROR_LOG(NETWORK, "%s: Winsock error: %d", (s) != NULL ? (s) : "", WSAGetLastError()); } while (false)
#ifndef SHUT_WR
#define SHUT_WR SD_SEND

View File

@ -534,7 +534,7 @@ private:
}
if (ev & PICO_SOCK_EV_ERR) {
INFO_LOG(MODEM, "TcpSocket[%s] Pico socket error received: %s", name.c_str(), strerror(pico_err));
INFO_LOG(NETWORK, "TcpSocket[%s] Pico socket error received: %s", name.c_str(), strerror(pico_err));
closeAll();
}
@ -630,7 +630,7 @@ public:
static_cast<TcpSink *>(picoSock->priv)->picoCallback(ev);
});
if (pico_sock == nullptr)
ERROR_LOG(MODEM, "error opening TCP socket: %s", strerror(pico_err));
ERROR_LOG(NETWORK, "error opening TCP socket: %s", strerror(pico_err));
pico_sock->priv = this;
int yes = 1;
pico_socket_setoption(pico_sock, PICO_TCP_NODELAY, &yes);
@ -638,9 +638,9 @@ public:
uint16_t listen_port = 0;
int ret = pico_socket_bind(pico_sock, &inaddr_any, &listen_port);
if (ret < 0)
ERROR_LOG(MODEM, "error binding TCP socket to port %u: %s", short_be(listen_port), strerror(pico_err));
ERROR_LOG(NETWORK, "error binding TCP socket to port %u: %s", short_be(listen_port), strerror(pico_err));
else if (pico_socket_listen(pico_sock, 10) != 0)
ERROR_LOG(MODEM, "error listening on port %u", short_be(listen_port));
ERROR_LOG(NETWORK, "error listening on port %u", short_be(listen_port));
}
~TcpSink() {
@ -734,10 +734,11 @@ public:
}
private:
UdpSocket(asio::io_context& io_context, u16 port, pico_socket *pico_sock)
UdpSocket(asio::io_context& io_context, u16 port, pico_socket *pico_sock, u16 dcport)
: io_context(io_context),
socket(io_context, asio::ip::udp::endpoint(asio::ip::udp::v4(), port)),
pico_sock(pico_sock)
pico_sock(pico_sock),
dcport(dcport)
{
asio::socket_base::broadcast option(true);
socket.set_option(option);
@ -754,7 +755,7 @@ private:
return;
}
DEBUG_LOG(NETWORK, "UdpSocket: received %d bytes to port %d from %s:%d", (int)len,
socket.local_endpoint().port(), source.address().to_string().c_str(), source.port());
dcport, source.address().to_string().c_str(), source.port());
if (len == 0)
WARN_LOG(NETWORK, "Received empty datagram");
@ -769,9 +770,9 @@ private:
msginfo.local_addr.ip4.addr = srcAddr;
msginfo.local_port = htons(source.port());
int r = pico_socket_sendto_extended(pico_sock, &recvbuf[0], len, &dcaddr, htons(socket.local_endpoint().port()), &msginfo);
int r = pico_socket_sendto_extended(pico_sock, &recvbuf[0], len, &dcaddr, htons(dcport), &msginfo);
if (r < (int)len)
INFO_LOG(MODEM, "error UDP sending to port %d: %s", socket.local_endpoint().port(), strerror(pico_err));
INFO_LOG(NETWORK, "error UDP sending to port %d: %s", dcport, strerror(pico_err));
}
readAsync();
});
@ -782,6 +783,7 @@ private:
pico_socket *pico_sock;
std::array<u8, 1510> recvbuf;
asio::ip::udp::endpoint source; // source endpoint when receiving packets
u16 dcport;
friend super;
};
@ -825,7 +827,16 @@ public:
if (it != sockets.end())
return it->second;
try {
UdpSocket::Ptr sock = UdpSocket::create(io_context, port, pico_sock);
UdpSocket::Ptr sock;
try {
sock = UdpSocket::create(io_context, port, pico_sock, port);
} catch (const std::system_error& e) {
if (e.code() != asio::error::address_in_use)
throw;
// Use a random local port
WARN_LOG(NETWORK, "Server UDP socket on port %d: address in use, using random port instead", port);
sock = UdpSocket::create(io_context, 0, pico_sock, port);
}
sock->start();
sockets[port] = sock;
return sock;
@ -985,9 +996,9 @@ private:
upnpCmd = std::async(std::launch::async, [this, port, udpOnly]()
{
if (!upnp->AddPortMapping(port, false))
WARN_LOG(MODEM, "UPNP AddPortMapping UDP %d failed", port);
WARN_LOG(NETWORK, "UPNP AddPortMapping UDP %d failed", port);
if (!udpOnly && !upnp->AddPortMapping(port, true))
WARN_LOG(MODEM, "UPNP AddPortMapping TCP %d failed", port);
WARN_LOG(NETWORK, "UPNP AddPortMapping TCP %d failed", port);
});
}
}
@ -1223,7 +1234,7 @@ void PicoThread::run()
{
if (settings.content.gameId == game.gameId[j])
{
NOTICE_LOG(MODEM, "Found network ports for game %s", settings.content.gameId.c_str());
NOTICE_LOG(NETWORK, "Found network ports for game %s", settings.content.gameId.c_str());
ports = &game;
break;
}
@ -1245,15 +1256,15 @@ void PicoThread::run()
// Initialize miniupnpc and map network ports
ThreadName _("UPNP-init");
if (!upnp->Init())
WARN_LOG(MODEM, "UPNP Init failed");
WARN_LOG(NETWORK, "UPNP Init failed");
else
{
for (u32 i = 0; i < std::size(ports->udpPorts) && ports->udpPorts[i] != 0; i++)
if (!upnp->AddPortMapping(ports->udpPorts[i], false))
WARN_LOG(MODEM, "UPNP AddPortMapping UDP %d failed", ports->udpPorts[i]);
WARN_LOG(NETWORK, "UPNP AddPortMapping UDP %d failed", ports->udpPorts[i]);
for (u32 i = 0; i < std::size(ports->tcpPorts) && ports->tcpPorts[i] != 0; i++)
if (!upnp->AddPortMapping(ports->tcpPorts[i], true))
WARN_LOG(MODEM, "UPNP AddPortMapping TCP %d failed", ports->tcpPorts[i]);
WARN_LOG(NETWORK, "UPNP AddPortMapping TCP %d failed", ports->tcpPorts[i]);
}
}));
}
@ -1280,14 +1291,14 @@ void PicoThread::run()
memcpy(&dnsaddr.addr, &endpoint.address().to_v4().to_bytes()[0], sizeof(dnsaddr.addr));
char s[17];
pico_ipv4_to_string(s, dnsaddr.addr);
NOTICE_LOG(MODEM, "%s IP is %s", dnsName.c_str(), s);
NOTICE_LOG(NETWORK, "%s IP is %s", dnsName.c_str(), s);
}
else
{
u32 addr;
pico_string_to_ipv4("46.101.91.123", &addr);
dnsaddr.addr = addr;
WARN_LOG(MODEM, "Can't resolve dns.flyca.st. Using default 46.101.91.123");
WARN_LOG(NETWORK, "Can't resolve dns.flyca.st. Using default 46.101.91.123");
}
}
resolveDns(*io_context);
@ -1350,7 +1361,7 @@ void PicoThread::run()
dhcpSettings.pool_next = addr;
dhcpSettings.dns_server.addr = dnsaddr.addr;
if (pico_dhcp_server_initiate(&dhcpSettings) != 0)
WARN_LOG(MODEM, "DHCP server init failed");
WARN_LOG(NETWORK, "DHCP server init failed");
}
// Create sinks