if (!quiche_version_is_supported(version)) {
DEBUGLOG("Unsupported version");
++frontend.d_doh3UnsupportedVersionErrors;
- handleVersionNegociation(sock, clientConnID, serverConnID, client, localAddr, buffer);
+ handleVersionNegotiation(sock, clientConnID, serverConnID, client, localAddr, buffer, clientState.local.isUnspecified());
continue;
}
if (token_len == 0) {
/* stateless retry */
DEBUGLOG("No token received");
- handleStatelessRetry(sock, clientConnID, serverConnID, client, localAddr, version, buffer);
+ handleStatelessRetry(sock, clientConnID, serverConnID, client, localAddr, version, buffer, clientState.local.isUnspecified());
continue;
}
processH3Events(clientState, frontend, conn->get(), client, serverConnID, buffer);
- flushEgress(sock, conn->get().d_conn, client, localAddr, buffer);
+ flushEgress(sock, conn->get().d_conn, client, localAddr, buffer, clientState.local.isUnspecified());
}
else {
DEBUGLOG("Connection not established");
for (auto conn = frontend->d_server_config->d_connections.begin(); conn != frontend->d_server_config->d_connections.end();) {
quiche_conn_on_timeout(conn->second.d_conn.get());
- flushEgress(sock, conn->second.d_conn, conn->second.d_peer, conn->second.d_localAddr, buffer);
+ flushEgress(sock, conn->second.d_conn, conn->second.d_peer, conn->second.d_localAddr, buffer, clientState->local.isUnspecified());
if (quiche_conn_is_closed(conn->second.d_conn.get())) {
#ifdef DEBUGLOG_ENABLED
}
}
-static void sendFromTo(Socket& sock, const ComboAddress& peer, const ComboAddress& local, PacketBuffer& buffer)
+static void sendFromTo(Socket& sock, const ComboAddress& peer, const ComboAddress& local, PacketBuffer& buffer, [[maybe_unused]] bool socketBoundToAny)
{
- const int flags = 0;
- if (local.sin4.sin_family == 0) {
+ /* we only want to specify the source address to use if we were able to
+ either harvest it from the incoming packet, or if our socket is already
+ bound to a specific address */
+ bool setSourceAddress = local.sin4.sin_family != 0;
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+ /* FreeBSD and DragonFlyBSD refuse the use of IP_SENDSRCADDR on a socket that is bound to a
+ specific address, returning EINVAL in that case. */
+ if (!socketBoundToAny) {
+ setSourceAddress = false;
+ }
+#endif /* __FreeBSD__ || __DragonFly__ */
+
+ if (!setSourceAddress) {
+ const int flags = 0;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
auto ret = sendto(sock.getHandle(), buffer.data(), buffer.size(), flags, reinterpret_cast<const struct sockaddr*>(&peer), peer.getSocklen());
if (ret < 0) {
}
}
-void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress& localAddr, uint32_t version, PacketBuffer& buffer)
+void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress& localAddr, uint32_t version, PacketBuffer& buffer, bool socketBoundToAny)
{
auto newServerConnID = getCID();
if (!newServerConnID) {
}
buffer.resize(static_cast<size_t>(written));
- sendFromTo(sock, peer, localAddr, buffer);
+ sendFromTo(sock, peer, localAddr, buffer, socketBoundToAny);
}
-void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress& localAddr, PacketBuffer& buffer)
+void handleVersionNegotiation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress& localAddr, PacketBuffer& buffer, bool socketBoundToAny)
{
buffer.resize(MAX_DATAGRAM_SIZE);
}
buffer.resize(static_cast<size_t>(written));
- sendFromTo(sock, peer, localAddr, buffer);
+ sendFromTo(sock, peer, localAddr, buffer, socketBoundToAny);
}
-void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer, const ComboAddress& localAddr, PacketBuffer& buffer)
+void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer, const ComboAddress& localAddr, PacketBuffer& buffer, bool socketBoundToAny)
{
buffer.resize(MAX_DATAGRAM_SIZE);
quiche_send_info send_info;
}
// FIXME pacing (as send_info.at should tell us when to send the packet) ?
buffer.resize(static_cast<size_t>(written));
- sendFromTo(sock, peer, localAddr, buffer);
+ sendFromTo(sock, peer, localAddr, buffer, socketBoundToAny);
}
}
This is indicated by setting the family to 0 which is acted upon
in sendUDPResponse() and DelayedPacket::().
*/
- const ComboAddress bogusV4("0.0.0.0:0");
- const ComboAddress bogusV6("[::]:0");
- if ((localAddr.sin4.sin_family == AF_INET && localAddr == bogusV4) || (localAddr.sin4.sin_family == AF_INET6 && localAddr == bogusV6)) {
+ if (localAddr.isUnspecified()) {
localAddr.sin4.sin_family = 0;
}
}
std::optional<PacketBuffer> getCID();
PacketBuffer mintToken(const PacketBuffer& dcid, const ComboAddress& peer);
std::optional<PacketBuffer> validateToken(const PacketBuffer& token, const ComboAddress& peer);
-void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress& localAddr, uint32_t version, PacketBuffer& buffer);
-void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress& localAddr, PacketBuffer& buffer);
-void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer, const ComboAddress& localAddr, PacketBuffer& buffer);
+void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress& localAddr, uint32_t version, PacketBuffer& buffer, bool socketBoundToAny);
+void handleVersionNegotiation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress& localAddr, PacketBuffer& buffer, bool socketBoundToAny);
+void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer, const ComboAddress& localAddr, PacketBuffer& buffer, bool socketBoundToAny);
void configureQuiche(QuicheConfig& config, const QuicheParams& params, bool isHTTP);
bool recvAsync(Socket& socket, PacketBuffer& buffer, ComboAddress& clientAddr, ComboAddress& localAddr);
std::string getSNIFromQuicheConnection(const QuicheConnection& conn);
if (!quiche_version_is_supported(version)) {
DEBUGLOG("Unsupported version");
++frontend.d_doqUnsupportedVersionErrors;
- handleVersionNegociation(sock, clientConnID, serverConnID, client, localAddr, buffer);
+ handleVersionNegotiation(sock, clientConnID, serverConnID, client, localAddr, buffer, clientState.local.isUnspecified());
continue;
}
if (token_len == 0) {
/* stateless retry */
DEBUGLOG("No token received");
- handleStatelessRetry(sock, clientConnID, serverConnID, client, localAddr, version, buffer);
+ handleStatelessRetry(sock, clientConnID, serverConnID, client, localAddr, version, buffer, clientState.local.isUnspecified());
continue;
}
handleReadableStream(frontend, clientState, *conn, streamID, client, serverConnID);
}
- flushEgress(sock, conn->get().d_conn, client, localAddr, buffer);
+ flushEgress(sock, conn->get().d_conn, client, localAddr, buffer, clientState.local.isUnspecified());
}
else {
DEBUGLOG("Connection not established");
for (auto conn = frontend->d_server_config->d_connections.begin(); conn != frontend->d_server_config->d_connections.end();) {
quiche_conn_on_timeout(conn->second.d_conn.get());
- flushEgress(sock, conn->second.d_conn, conn->second.d_peer, conn->second.d_localAddr, buffer);
+ flushEgress(sock, conn->second.d_conn, conn->second.d_peer, conn->second.d_localAddr, buffer, clientState->local.isUnspecified());
if (quiche_conn_is_closed(conn->second.d_conn.get())) {
#ifdef DEBUGLOG_ENABLED
return true;
}
+ [[nodiscard]] bool isUnspecified() const
+ {
+ const ComboAddress unspecifiedV4("0.0.0.0:0");
+ const ComboAddress unspecifiedV6("[::]:0");
+ return *this == unspecifiedV4 || *this == unspecifiedV6;
+ }
+
[[nodiscard]] ComboAddress mapToIPv4() const
{
if (!isMappedIPv4()) {