void CommunicatorClass::makeNotifySockets()
{
if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
- d_nsock4 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0), true, ::arg().mustDo("non-local-bind"));
+ d_nsock4 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0).d_address, true, ::arg().mustDo("non-local-bind"));
}
else {
d_nsock4 = -1;
}
if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
- d_nsock6 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0), true, ::arg().mustDo("non-local-bind"));
+ d_nsock6 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0).d_address, true, ::arg().mustDo("non-local-bind"));
}
else {
d_nsock6 = -1;
ctx.slog->info(Logr::Warning, "XFR: unable to xfr, address family is not enabled for outgoing traffic (query-local-address)", "address family", Logging::Loggable(isV6 ? "IPv6" : "IPv4")));
return;
}
- laddr = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
+ laddr = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0).d_address;
}
bool hadDnssecZone = false;
return;
}
- try {
- sendMsgWithOptions(sock, buffer.data(), buffer.size(), &dest, &from, 0, 0);
- }
- catch (const std::exception& exp) {
- VERBOSESLOG(infolog("Error sending UDP response from %s to %s: %s", from.toStringWithPort(), dest.toStringWithPort(), exp.what()),
- dnsdist::logging::getTopLogger("sendfromto")->error(Logr::Info, exp.what(), "Error sending UDP response", "source.address", Logging::Loggable(from), "client.address", Logging::Loggable(dest)));
+ auto ret = sendMsgWithOptions(sock, buffer.data(), buffer.size(), &dest, &from, 0, 0);
+ if (!ret.has_value()) {
+ VERBOSESLOG(infolog("Error sending UDP response from %s to %s: %s", from.toStringWithPort(), dest.toStringWithPort(), stringerror(ret.error())),
+ dnsdist::logging::getTopLogger("sendfromto")->error(Logr::Info, ret.error(), "Error sending UDP response", "source.address", Logging::Loggable(from), "client.address", Logging::Loggable(dest)));
}
}
return;
}
- try {
- sendMsgWithOptions(sock.getHandle(), buffer.data(), buffer.size(), &peer, &local, 0, 0);
- }
- catch (const std::exception& exp) {
- VERBOSESLOG(infolog("Error while sending QUIC datagram of size %d from %s to %s: %s", buffer.size(), local.toStringWithPort(), peer.toStringWithPort(), exp.what()),
- dnsdist::logging::getTopLogger("quic-send-from-to")->error(Logr::Info, exp.what(), "Error while sending QUIC datagram", "datagram_size", Logging::Loggable(buffer.size()), "source.address", Logging::Loggable(local), "client.address", Logging::Loggable(peer)));
+ auto ret = sendMsgWithOptions(sock.getHandle(), buffer.data(), buffer.size(), &peer, &local, 0, 0);
+
+ if (!ret.has_value()) {
+ VERBOSESLOG(infolog("Error while sending QUIC datagram of size %d from %s to %s: %s", buffer.size(), local.toStringWithPort(), peer.toStringWithPort(), stringerror(ret.error())),
+ dnsdist::logging::getTopLogger("quic-send-from-to")->error(Logr::Info, ret.error(), "Error while sending QUIC datagram", "datagram_size", Logging::Loggable(buffer.size()), "source.address", Logging::Loggable(local), "client.address", Logging::Loggable(peer)));
}
}
*place &= (~((1 << bitsleft) - 1));
}
-size_t sendMsgWithOptions(int socketDesc, const void* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags)
+pdns::expected<size_t, int> sendMsgWithOptions(int socketDesc, const void* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags)
{
msghdr msgh{};
iovec iov{};
iov.iov_base = reinterpret_cast<void*>(reinterpret_cast<char*>(iov.iov_base) + written);
}
else if (res == 0) {
- return res;
+ return static_cast<size_t>(0);
}
else if (res == -1) {
int err = errno;
especially with TCP Fast Open */
return sent;
}
- unixDie("failed in sendMsgWithOptions");
+ return pdns::unexpected{err};
}
} while (true);
#include <netdb.h>
#include <sstream>
#include <sys/un.h>
+#include <variant>
#include "namespaces.hh"
bool HarvestTimestamp(struct msghdr* msgh, struct timeval* timeval);
void fillMSGHdr(struct msghdr* msgh, struct iovec* iov, cmsgbuf_aligned* cbuf, size_t cbufsize, char* data, size_t datalen, ComboAddress* addr);
int sendOnNBSocket(int fileDesc, const struct msghdr* msgh);
-size_t sendMsgWithOptions(int socketDesc, const void* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags);
+
+// A poor man's std::expected, which only becomes available for real with C++23
+namespace pdns
+{
+template <class E>
+class unexpected
+{
+public:
+ unexpected(const E& arg) :
+ err(arg) {}
+ const E& error() const
+ {
+ return err;
+ }
+
+private:
+ E err;
+};
+
+template <class T, class E>
+class expected : private std::variant<T, E>
+{
+public:
+ expected(const T& arg) :
+ std::variant<T, E>(arg) {}
+
+ expected(const unexpected<E>& arg) :
+ std::variant<T, E>(arg.error()) {}
+
+ [[nodiscard]] bool has_value() const
+ {
+ return std::holds_alternative<T>(*this);
+ }
+
+ const T& value() const
+ {
+ return std::get<T>(*this);
+ }
+ const E& error() const
+ {
+ return std::get<E>(*this);
+ }
+};
+}
+
+[[nodiscard]] pdns::expected<size_t, int> sendMsgWithOptions(int socketDesc, 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);
static void communicatorThread()
{
setThreadName("ixfrdist/communicator");
- auto sock4 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0), true);
- auto sock6 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0), true);
+ auto sock4 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0).d_address, true);
+ auto sock6 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0).d_address, true);
if (sock4 < 0) {
throw std::runtime_error("Unable to create local query socket");
// fatals (with calling exit!) on some error conditions. This all looks fragile, but there are
// existing callers, changing sendMsgWithOption() to return ssize_t to mkae it more sned(2) like
// needs to be done with extra care.
- sent = sendMsgWithOptions(*fileDesc, data, len, nullptr, &local, interface->d_index, 0);
+
+ auto sendRet = sendMsgWithOptions(*fileDesc, data, len, nullptr, &local, interface->d_index, 0);
+ if (sendRet.has_value()) {
+ sent = static_cast<ssize_t>(sendRet.value());
+ }
+ else {
+ sent = sendRet.error();
+ }
}
if (sent < 0) {
int tmp = errno;
if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
continue;
}
- auto local = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
+ auto local = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0).d_address;
ctx.sock = makeQuerySocket(local, false); // create TCP socket. RFC2136 section 6.2 seems to be ok with this.
if (ctx.sock < 0) {
SLOG(g_log << Logger::Error << ctx.msgPrefix << "Error creating socket: " << stringerror() << endl,
#ifdef MSG_FASTOPEN
if (d_fastOpen) {
int socketFlags = MSG_FASTOPEN;
- size_t sent = sendMsgWithOptions(d_socket, reinterpret_cast<const char *>(&buffer.at(pos)), toWrite - pos, &d_remote, nullptr, 0, socketFlags);
- if (sent > 0) {
+ auto sendRet = sendMsgWithOptions(d_socket, reinterpret_cast<const char*>(&buffer.at(pos)), toWrite - pos, &d_remote, nullptr, 0, socketFlags);
+ if (!sendRet.has_value()) {
+ throw std::runtime_error("sendMsgWithOptions: " + stringerror(sendRet.error()));
+ }
+ if (sendRet.value() > 0) {
d_fastOpen = false;
- pos += sent;
+ pos += sendRet.value();
}
if (pos < toWrite) {
#ifdef MSG_FASTOPEN
if (d_fastOpen) {
int socketFlags = MSG_FASTOPEN;
- size_t sent = sendMsgWithOptions(d_socket, reinterpret_cast<const char *>(buffer), bufferSize, &d_remote, nullptr, 0, socketFlags);
- if (sent > 0) {
+ auto sendRet = sendMsgWithOptions(d_socket, reinterpret_cast<const char*>(buffer), bufferSize, &d_remote, nullptr, 0, socketFlags);
+ if (!sendRet.has_value()) {
+ throw std::runtime_error("sendMsgWithOptions: " + stringerror(sendRet.error()));
+ }
+ if (sendRet.value() > 0) {
d_fastOpen = false;
}
- return sent;
+ return sendRet.value();
}
#endif /* MSG_FASTOPEN */
}
}
+// Check the tricky case: two somewhat compatibe types
+BOOST_AUTO_TEST_CASE(test_expected)
+{
+ pdns::expected<size_t, int> test(0);
+
+ test = static_cast<size_t>(1);
+ BOOST_ASSERT(test.has_value());
+ BOOST_CHECK_EQUAL(test.value(), 1U);
+
+ test = 2;
+ BOOST_ASSERT(test.has_value());
+ BOOST_CHECK_EQUAL(test.value(), 2U);
+
+ test = pdns::unexpected(3);
+ BOOST_ASSERT(!test.has_value());
+ BOOST_CHECK_EQUAL(test.error(), 3);
+}
+
BOOST_AUTO_TEST_SUITE_END()