And expose the correct destination IP to Lua.
static size_t const s_initialUDPPacketBufferSize = s_maxUDPResponsePacketSize + DNSCRYPT_MAX_RESPONSE_PADDING_AND_MAC_SIZE;
static_assert(s_initialUDPPacketBufferSize <= UINT16_MAX, "Packet size should fit in a uint16_t");
-static ssize_t sendfromto(int sock, const void* data, size_t len, int flags, const ComboAddress& from, const ComboAddress& dest)
+static ssize_t sendfromto(int sock, const PacketBuffer& buffer, const ComboAddress& from, const ComboAddress& dest)
{
+ const int flags = 0;
if (from.sin4.sin_family == 0) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- return sendto(sock, data, len, flags, reinterpret_cast<const struct sockaddr*>(&dest), dest.getSocklen());
- }
- msghdr msgh{};
- iovec iov{};
- cmsgbuf_aligned cbuf;
-
- /* Set up iov and msgh structures. */
- memset(&msgh, 0, sizeof(struct msghdr));
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): it's the API
- iov.iov_base = const_cast<void*>(data);
- iov.iov_len = len;
- msgh.msg_iov = &iov;
- msgh.msg_iovlen = 1;
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-const-cast)
- msgh.msg_name = const_cast<sockaddr*>(reinterpret_cast<const sockaddr*>(&dest));
- msgh.msg_namelen = dest.getSocklen();
-
- if (from.sin4.sin_family != 0) {
- addCMsgSrcAddr(&msgh, &cbuf, &from, 0);
+ return sendto(sock, buffer.data(), buffer.size(), flags, reinterpret_cast<const struct sockaddr*>(&dest), dest.getSocklen());
}
- else {
- msgh.msg_control = nullptr;
- }
- return sendmsg(sock, &msgh, flags);
+ return sendMsgWithOptions(sock, buffer.data(), buffer.size(), &dest, &from, 0, 0);
}
static void truncateTC(PacketBuffer& packet, size_t maximumSize, unsigned int qnameWireLength)
ComboAddress origDest;
void operator()()
{
- ssize_t res = sendfromto(fd, packet.data(), packet.size(), 0, origDest, destination);
+ ssize_t res = sendfromto(fd, packet, origDest, destination);
if (res == -1) {
int err = errno;
vinfolog("Error sending delayed response to %s: %s", destination.toStringWithPort(), stringerror(err));
return true;
}
#endif /* DISABLE_DELAY_PIPE */
- // NOLINTNEXTLINE(readability-suspicious-call-argument)
- ssize_t res = sendfromto(origFD, response.data(), response.size(), 0, origDest, origRemote);
+ ssize_t res = sendfromto(origFD, response, origDest, origRemote);
if (res == -1) {
int err = errno;
vinfolog("Error sending response to %s: %s", origRemote.toStringWithPort(), stringerror(err));
class H3Connection
{
public:
- H3Connection(const ComboAddress& peer, QuicheConfig config, QuicheConnection&& conn) :
- d_peer(peer), d_conn(std::move(conn)), d_config(std::move(config))
+ H3Connection(const ComboAddress& peer, const ComboAddress& localAddr, QuicheConfig config, QuicheConnection&& conn) :
+ d_peer(peer), d_localAddr(localAddr), d_conn(std::move(conn)), d_config(std::move(config))
{
}
H3Connection(const H3Connection&) = delete;
~H3Connection() = default;
ComboAddress d_peer;
+ ComboAddress d_localAddr;
QuicheConnection d_conn;
QuicheConfig d_config;
QuicheHTTP3Connection d_http3{nullptr, quiche_h3_conn_free};
}
}
-static std::optional<std::reference_wrapper<H3Connection>> createConnection(DOH3ServerConfig& config, const PacketBuffer& serverSideID, const PacketBuffer& originalDestinationID, const ComboAddress& local, const ComboAddress& peer)
+static std::optional<std::reference_wrapper<H3Connection>> createConnection(DOH3ServerConfig& config, const PacketBuffer& serverSideID, const PacketBuffer& originalDestinationID, const ComboAddress& localAddr, const ComboAddress& peer)
{
auto quicheConfig = std::atomic_load_explicit(&config.config, std::memory_order_acquire);
auto quicheConn = QuicheConnection(quiche_accept(serverSideID.data(), serverSideID.size(),
originalDestinationID.data(), originalDestinationID.size(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- reinterpret_cast<const struct sockaddr*>(&local),
- local.getSocklen(),
+ reinterpret_cast<const struct sockaddr*>(&localAddr),
+ localAddr.getSocklen(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
reinterpret_cast<const struct sockaddr*>(&peer),
peer.getSocklen(),
quiche_conn_set_keylog_path(quicheConn.get(), config.df->d_quicheParams.d_keyLogFile.c_str());
}
- auto conn = H3Connection(peer, std::move(quicheConfig), std::move(quicheConn));
+ auto conn = H3Connection(peer, localAddr, std::move(quicheConfig), std::move(quicheConn));
auto pair = config.d_connections.emplace(serverSideID, std::move(conn));
return pair.first->second;
}
return;
}
DEBUGLOG("Dispatching GET query");
- doh3_dispatch_query(*(frontend.d_server_config), std::move(*payload), clientState.local, client, serverConnID, streamID);
+ doh3_dispatch_query(*(frontend.d_server_config), std::move(*payload), conn.d_localAddr, client, serverConnID, streamID);
conn.d_streamBuffers.erase(streamID);
conn.d_headersBuffers.erase(streamID);
return;
}
DEBUGLOG("Dispatching POST query");
- doh3_dispatch_query(*(frontend.d_server_config), std::move(streamBuffer), clientState.local, client, serverConnID, streamID);
+ doh3_dispatch_query(*(frontend.d_server_config), std::move(streamBuffer), conn.d_localAddr, client, serverConnID, streamID);
conn.d_headersBuffers.erase(streamID);
conn.d_streamBuffers.erase(streamID);
}
PacketBuffer tokenBuf;
while (true) {
ComboAddress client;
+ ComboAddress localAddr;
+ client.sin4.sin_family = clientState.local.sin4.sin_family;
+ localAddr.sin4.sin_family = clientState.local.sin4.sin_family;
buffer.resize(4096);
- if (!sock.recvFromAsync(buffer, client) || buffer.empty()) {
+ if (!dnsdist::doq::recvAsync(sock, buffer, client, localAddr)) {
return;
}
+ if (localAddr.sin4.sin_family == 0) {
+ localAddr = clientState.local;
+ }
+ else {
+ /* we don't get the port, only the address */
+ localAddr.sin4.sin_port = clientState.local.sin4.sin_port;
+ }
+
DEBUGLOG("Received DoH3 datagram of size " << buffer.size() << " from " << client.toStringWithPort());
uint32_t version{0};
if (!quiche_version_is_supported(version)) {
DEBUGLOG("Unsupported version");
++frontend.d_doh3UnsupportedVersionErrors;
- handleVersionNegociation(sock, clientConnID, serverConnID, client, buffer);
+ handleVersionNegociation(sock, clientConnID, serverConnID, client, localAddr, buffer);
continue;
}
if (token_len == 0) {
/* stateless retry */
DEBUGLOG("No token received");
- handleStatelessRetry(sock, clientConnID, serverConnID, client, version, buffer);
+ handleStatelessRetry(sock, clientConnID, serverConnID, client, localAddr, version, buffer);
continue;
}
}
DEBUGLOG("Creating a new connection");
- conn = createConnection(*frontend.d_server_config, serverConnID, *originalDestinationID, clientState.local, client);
+ conn = createConnection(*frontend.d_server_config, serverConnID, *originalDestinationID, localAddr, client);
if (!conn) {
continue;
}
reinterpret_cast<struct sockaddr*>(&client),
client.getSocklen(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- reinterpret_cast<struct sockaddr*>(&clientState.local),
- clientState.local.getSocklen(),
+ reinterpret_cast<struct sockaddr*>(&localAddr),
+ localAddr.getSocklen(),
};
auto done = quiche_conn_recv(conn->get().d_conn.get(), buffer.data(), buffer.size(), &recv_info);
processH3Events(clientState, frontend, conn->get(), client, serverConnID, buffer);
- flushEgress(sock, conn->get().d_conn, client, buffer);
+ flushEgress(sock, conn->get().d_conn, client, localAddr, buffer);
}
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, buffer);
+ flushEgress(sock, conn->second.d_conn, conn->second.d_peer, conn->second.d_localAddr, buffer);
if (quiche_conn_is_closed(conn->second.d_conn.get())) {
#ifdef DEBUGLOG_ENABLED
}
}
-void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, uint32_t version, PacketBuffer& buffer)
+static ssize_t sendFromTo(Socket& sock, const ComboAddress& peer, const ComboAddress& local, PacketBuffer& buffer)
+{
+ const int flags = 0;
+ if (local.sin4.sin_family == 0) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ return sendto(sock.getHandle(), buffer.data(), buffer.size(), flags, reinterpret_cast<const struct sockaddr*>(&peer), peer.getSocklen());
+ }
+
+ return sendMsgWithOptions(sock.getHandle(), buffer.data(), buffer.size(), &peer, &local, 0, 0);
+}
+
+void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress& localAddr, uint32_t version, PacketBuffer& buffer)
{
auto newServerConnID = getCID();
if (!newServerConnID) {
return;
}
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- sock.sendTo(reinterpret_cast<const char*>(buffer.data()), static_cast<size_t>(written), peer);
+ buffer.resize(static_cast<size_t>(written));
+ sendFromTo(sock, peer, localAddr, buffer);
}
-void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, PacketBuffer& buffer)
+void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress& localAddr, PacketBuffer& buffer)
{
buffer.resize(MAX_DATAGRAM_SIZE);
DEBUGLOG("failed to create vneg packet " << written);
return;
}
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- sock.sendTo(reinterpret_cast<const char*>(buffer.data()), static_cast<size_t>(written), peer);
+
+ buffer.resize(static_cast<size_t>(written));
+ sendFromTo(sock, peer, localAddr, buffer);
}
-void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer, PacketBuffer& buffer)
+void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer, const ComboAddress& localAddr, PacketBuffer& buffer)
{
buffer.resize(MAX_DATAGRAM_SIZE);
quiche_send_info send_info;
return;
}
// FIXME pacing (as send_info.at should tell us when to send the packet) ?
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- sock.sendTo(reinterpret_cast<const char*>(buffer.data()), static_cast<size_t>(written), peer);
+ buffer.resize(static_cast<size_t>(written));
+ sendFromTo(sock, peer, localAddr, buffer);
}
}
}
}
+bool recvAsync(Socket& socket, PacketBuffer& buffer, ComboAddress& clientAddr, ComboAddress& localAddr)
+{
+ msghdr msgh{};
+ iovec iov{};
+ /* used by HarvestDestinationAddress */
+ cmsgbuf_aligned cbuf;
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), reinterpret_cast<char*>(&buffer.at(0)), buffer.size(), &clientAddr);
+
+ ssize_t got = recvmsg(socket.getHandle(), &msgh, 0);
+ if (got < 0) {
+ int error = errno;
+ if (error != EAGAIN) {
+ throw NetworkError("Error in recvmsg: " + stringerror(error));
+ }
+ return false;
+ }
+
+ if ((msgh.msg_flags & MSG_TRUNC) != 0) {
+ return false;
+ }
+
+ buffer.resize(static_cast<size_t>(got));
+
+ if (HarvestDestinationAddress(&msgh, &localAddr)) {
+ /* so it turns out that sometimes the kernel lies to us:
+ the address is set to 0.0.0.0:0 which makes our sendfromto() use
+ the wrong address. In that case it's better to let the kernel
+ do the work by itself and use sendto() instead.
+ 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)) {
+ localAddr.sin4.sin_family = 0;
+ }
+ }
+ else {
+ localAddr.sin4.sin_family = 0;
+ }
+
+ return !buffer.empty();
+}
+
};
#endif
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, uint32_t version, PacketBuffer& buffer);
-void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, PacketBuffer& buffer);
-void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer, PacketBuffer& buffer);
+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 configureQuiche(QuicheConfig& config, const QuicheParams& params, bool isHTTP);
+bool recvAsync(Socket& socket, PacketBuffer& buffer, ComboAddress& clientAddr, ComboAddress& localAddr);
};
class Connection
{
public:
- Connection(const ComboAddress& peer, QuicheConfig config, QuicheConnection conn) :
- d_peer(peer), d_conn(std::move(conn)), d_config(std::move(config))
+ Connection(const ComboAddress& peer, const ComboAddress& localAddr, QuicheConfig config, QuicheConnection conn) :
+ d_peer(peer), d_localAddr(localAddr), d_conn(std::move(conn)), d_config(std::move(config))
{
}
Connection(const Connection&) = delete;
~Connection() = default;
ComboAddress d_peer;
+ ComboAddress d_localAddr;
QuicheConnection d_conn;
QuicheConfig d_config;
}
}
-static std::optional<std::reference_wrapper<Connection>> createConnection(DOQServerConfig& config, const PacketBuffer& serverSideID, const PacketBuffer& originalDestinationID, const ComboAddress& local, const ComboAddress& peer)
+static std::optional<std::reference_wrapper<Connection>> createConnection(DOQServerConfig& config, const PacketBuffer& serverSideID, const PacketBuffer& originalDestinationID, const ComboAddress& peer, const ComboAddress& localAddr)
{
auto quicheConfig = std::atomic_load_explicit(&config.config, std::memory_order_acquire);
auto quicheConn = QuicheConnection(quiche_accept(serverSideID.data(), serverSideID.size(),
originalDestinationID.data(), originalDestinationID.size(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- reinterpret_cast<const struct sockaddr*>(&local),
- local.getSocklen(),
+ reinterpret_cast<const struct sockaddr*>(&localAddr),
+ localAddr.getSocklen(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
reinterpret_cast<const struct sockaddr*>(&peer),
peer.getSocklen(),
quiche_conn_set_keylog_path(quicheConn.get(), config.df->d_quicheParams.d_keyLogFile.c_str());
}
- auto conn = Connection(peer, std::move(quicheConfig), std::move(quicheConn));
+ auto conn = Connection(peer, localAddr, std::move(quicheConfig), std::move(quicheConn));
auto pair = config.d_connections.emplace(serverSideID, std::move(conn));
return pair.first->second;
}
return;
}
DEBUGLOG("Dispatching query");
- doq_dispatch_query(*(frontend.d_server_config), std::move(streamBuffer), clientState.local, client, serverConnID, streamID);
+ doq_dispatch_query(*(frontend.d_server_config), std::move(streamBuffer), conn.d_localAddr, client, serverConnID, streamID);
conn.d_streamBuffers.erase(streamID);
}
PacketBuffer tokenBuf;
while (true) {
ComboAddress client;
+ ComboAddress localAddr;
+ client.sin4.sin_family = clientState.local.sin4.sin_family;
+ localAddr.sin4.sin_family = clientState.local.sin4.sin_family;
buffer.resize(4096);
- if (!sock.recvFromAsync(buffer, client) || buffer.empty()) {
+ if (!dnsdist::doq::recvAsync(sock, buffer, client, localAddr)) {
return;
}
+ if (localAddr.sin4.sin_family == 0) {
+ localAddr = clientState.local;
+ }
+ else {
+ /* we don't get the port, only the address */
+ localAddr.sin4.sin_port = clientState.local.sin4.sin_port;
+ }
+
DEBUGLOG("Received DoQ datagram of size " << buffer.size() << " from " << client.toStringWithPort());
uint32_t version{0};
if (!quiche_version_is_supported(version)) {
DEBUGLOG("Unsupported version");
++frontend.d_doqUnsupportedVersionErrors;
- handleVersionNegociation(sock, clientConnID, serverConnID, client, buffer);
+ handleVersionNegociation(sock, clientConnID, serverConnID, client, localAddr, buffer);
continue;
}
if (token_len == 0) {
/* stateless retry */
DEBUGLOG("No token received");
- handleStatelessRetry(sock, clientConnID, serverConnID, client, version, buffer);
+ handleStatelessRetry(sock, clientConnID, serverConnID, client, localAddr, version, buffer);
continue;
}
}
DEBUGLOG("Creating a new connection");
- conn = createConnection(*frontend.d_server_config, serverConnID, *originalDestinationID, clientState.local, client);
+ conn = createConnection(*frontend.d_server_config, serverConnID, *originalDestinationID, client, localAddr);
if (!conn) {
continue;
}
reinterpret_cast<struct sockaddr*>(&client),
client.getSocklen(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- reinterpret_cast<struct sockaddr*>(&clientState.local),
- clientState.local.getSocklen(),
+ reinterpret_cast<struct sockaddr*>(&localAddr),
+ localAddr.getSocklen(),
};
auto done = quiche_conn_recv(conn->get().d_conn.get(), buffer.data(), buffer.size(), &recv_info);
handleReadableStream(frontend, clientState, *conn, streamID, client, serverConnID);
}
- flushEgress(sock, conn->get().d_conn, client, buffer);
+ flushEgress(sock, conn->get().d_conn, client, localAddr, buffer);
}
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, buffer);
+ flushEgress(sock, conn->second.d_conn, conn->second.d_peer, conn->second.d_localAddr, buffer);
if (quiche_conn_is_closed(conn->second.d_conn.get())) {
#ifdef DEBUGLOG_ENABLED
*place &= (~((1<<bitsleft)-1));
}
-size_t sendMsgWithOptions(int fd, const char* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags)
+size_t sendMsgWithOptions(int fd, const void* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags)
{
- struct msghdr msgh;
- struct iovec iov;
+ msghdr msgh{};
+ iovec iov{};
cmsgbuf_aligned cbuf;
/* Set up iov and msgh structures. */
- memset(&msgh, 0, sizeof(struct msghdr));
+ memset(&msgh, 0, sizeof(msgh));
msgh.msg_control = nullptr;
msgh.msg_controllen = 0;
if (dest) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-const-cast): it's the API
msgh.msg_name = reinterpret_cast<void*>(const_cast<ComboAddress*>(dest));
msgh.msg_namelen = dest->getSocklen();
}
msgh.msg_flags = 0;
- if (localItf != 0 && local) {
+ if (local && local->sin4.sin_family != 0) {
addCMsgSrcAddr(&msgh, &cbuf, local, localItf);
}
- iov.iov_base = reinterpret_cast<void*>(const_cast<char*>(buffer));
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): it's the API
+ iov.iov_base = const_cast<void*>(buffer);
iov.iov_len = len;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
firstTry = false;
#endif
iov.iov_len -= written;
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): it's the API
iov.iov_base = reinterpret_cast<void*>(reinterpret_cast<char*>(iov.iov_base) + written);
}
else if (res == 0) {
return sent;
}
else {
- unixDie("failed in sendMsgWithTimeout");
+ unixDie("failed in sendMsgWithOptions");
}
}
}
bool HarvestTimestamp(struct msghdr* msgh, struct timeval* tv);
void fillMSGHdr(struct msghdr* msgh, struct iovec* iov, cmsgbuf_aligned* cbuf, size_t cbufsize, char* data, size_t datalen, ComboAddress* addr);
int sendOnNBSocket(int fd, const struct msghdr *msgh);
-size_t sendMsgWithOptions(int fd, const char* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags);
+size_t sendMsgWithOptions(int fd, const void* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags);
/* requires a non-blocking, connected TCP socket */
bool isTCPSocketUsable(int sock);