Merge pull request #746 from teg/resolved-connect-udp
resolved: UDP fixes
This commit is contained in:
+6
-1
@@ -954,7 +954,12 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* https://tools.ietf.org/html/rfc4648#section-6 */
|
||||
/* https://tools.ietf.org/html/rfc4648#section-6
|
||||
* Notice that base32hex differs from base32 in the alphabet it uses.
|
||||
* The distinction is that the base32hex representation preserves the
|
||||
* order of the underlying data when compared as bytestrings, this is
|
||||
* useful when representing NSEC3 hashes, as one can then verify the
|
||||
* order of hashes directly from their representation. */
|
||||
char base32hexchar(int x) {
|
||||
static const char table[32] = "0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUV";
|
||||
|
||||
@@ -125,18 +125,17 @@ void dns_scope_next_dns_server(DnsScope *s) {
|
||||
manager_next_dns_server(s->manager);
|
||||
}
|
||||
|
||||
int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **server) {
|
||||
DnsServer *srv = NULL;
|
||||
int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) {
|
||||
union in_addr_union addr;
|
||||
int ifindex = 0, r;
|
||||
int family;
|
||||
uint16_t port;
|
||||
uint32_t mtu;
|
||||
int fd;
|
||||
|
||||
assert(s);
|
||||
assert(p);
|
||||
assert(p->protocol == s->protocol);
|
||||
assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0));
|
||||
|
||||
if (s->link) {
|
||||
mtu = s->link->mtu;
|
||||
@@ -148,28 +147,15 @@ int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **ser
|
||||
if (DNS_PACKET_QDCOUNT(p) > 1)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
srv = dns_scope_get_dns_server(s);
|
||||
if (!srv)
|
||||
return -ESRCH;
|
||||
|
||||
family = srv->family;
|
||||
addr = srv->address;
|
||||
port = 53;
|
||||
|
||||
if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (p->size + UDP_PACKET_HEADER_SIZE > mtu)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (family == AF_INET)
|
||||
fd = transaction_dns_ipv4_fd(t);
|
||||
else if (family == AF_INET6)
|
||||
fd = transaction_dns_ipv6_fd(t);
|
||||
else
|
||||
return -EAFNOSUPPORT;
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
r = manager_write(s->manager, fd, p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
} else if (s->protocol == DNS_PROTOCOL_LLMNR) {
|
||||
|
||||
@@ -192,20 +178,17 @@ int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **ser
|
||||
return -EAFNOSUPPORT;
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (server)
|
||||
*server = srv;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
|
||||
static int dns_scope_socket(DnsScope *s, int type, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
|
||||
DnsServer *srv = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
union sockaddr_union sa = {};
|
||||
@@ -249,13 +232,15 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
|
||||
fd = socket(sa.sa.sa_family, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
if (type == SOCK_STREAM) {
|
||||
r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (s->link) {
|
||||
uint32_t ifindex = htobe32(s->link->ifindex);
|
||||
@@ -298,6 +283,14 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server) {
|
||||
return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, 53, server);
|
||||
}
|
||||
|
||||
int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
|
||||
return dns_scope_socket(s, SOCK_STREAM, family, address, port, server);
|
||||
}
|
||||
|
||||
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
|
||||
char **i;
|
||||
|
||||
@@ -687,7 +680,7 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = dns_scope_emit(scope, NULL, p, NULL);
|
||||
r = dns_scope_emit(scope, -1, p);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to send conflict packet: %m");
|
||||
}
|
||||
|
||||
@@ -65,8 +65,9 @@ struct DnsScope {
|
||||
int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family);
|
||||
DnsScope* dns_scope_free(DnsScope *s);
|
||||
|
||||
int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **server);
|
||||
int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p);
|
||||
int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server);
|
||||
int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server);
|
||||
|
||||
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain);
|
||||
int dns_scope_good_key(DnsScope *s, DnsResourceKey *key);
|
||||
|
||||
@@ -39,10 +39,8 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
|
||||
dns_packet_unref(t->received);
|
||||
dns_answer_unref(t->cached);
|
||||
|
||||
sd_event_source_unref(t->dns_ipv4_event_source);
|
||||
sd_event_source_unref(t->dns_ipv6_event_source);
|
||||
safe_close(t->dns_ipv4_fd);
|
||||
safe_close(t->dns_ipv6_fd);
|
||||
sd_event_source_unref(t->dns_event_source);
|
||||
safe_close(t->dns_fd);
|
||||
|
||||
dns_server_unref(t->server);
|
||||
dns_stream_free(t->stream);
|
||||
@@ -94,7 +92,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) {
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
t->dns_ipv4_fd = t->dns_ipv6_fd = -1;
|
||||
t->dns_fd = -1;
|
||||
|
||||
t->question = dns_question_ref(q);
|
||||
|
||||
@@ -245,7 +243,7 @@ static int on_stream_complete(DnsStream *s, int error) {
|
||||
}
|
||||
|
||||
static int dns_transaction_open_tcp(DnsTransaction *t) {
|
||||
_cleanup_(dns_server_unrefp) DnsServer *server = NULL;
|
||||
DnsServer *server = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
@@ -310,6 +308,16 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dns_transaction_next_dns_server(DnsTransaction *t) {
|
||||
assert(t);
|
||||
|
||||
t->server = dns_server_unref(t->server);
|
||||
t->dns_event_source = sd_event_source_unref(t->dns_event_source);
|
||||
t->dns_fd = safe_close(t->dns_fd);
|
||||
|
||||
dns_scope_next_dns_server(t->scope);
|
||||
}
|
||||
|
||||
void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
||||
int r;
|
||||
|
||||
@@ -342,24 +350,6 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
||||
}
|
||||
}
|
||||
|
||||
if (t->scope->protocol == DNS_PROTOCOL_DNS) {
|
||||
|
||||
/* For DNS we are fine with accepting packets on any
|
||||
* interface, but the source IP address must be the
|
||||
* one of the DNS server we queried */
|
||||
|
||||
assert(t->server);
|
||||
|
||||
if (t->server->family != p->family)
|
||||
return;
|
||||
|
||||
if (!in_addr_equal(p->family, &p->sender, &t->server->address))
|
||||
return;
|
||||
|
||||
if (p->sender_port != 53)
|
||||
return;
|
||||
}
|
||||
|
||||
if (t->received != p) {
|
||||
dns_packet_unref(t->received);
|
||||
t->received = dns_packet_ref(p);
|
||||
@@ -396,7 +386,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
||||
}
|
||||
|
||||
/* On DNS, couldn't send? Try immediately again, with a new server */
|
||||
dns_scope_next_dns_server(t->scope);
|
||||
dns_transaction_next_dns_server(t);
|
||||
|
||||
r = dns_transaction_go(t);
|
||||
if (r < 0) {
|
||||
@@ -431,6 +421,56 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
|
||||
}
|
||||
|
||||
static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
DnsTransaction *t = userdata;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
assert(t->scope);
|
||||
|
||||
r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (dns_packet_validate_reply(p) > 0 &&
|
||||
DNS_PACKET_ID(p) == t->id) {
|
||||
dns_transaction_process_reply(t, p);
|
||||
} else
|
||||
log_debug("Invalid DNS packet.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dns_transaction_emit(DnsTransaction *t) {
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
|
||||
if (t->scope->protocol == DNS_PROTOCOL_DNS && !t->server) {
|
||||
DnsServer *server = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
fd = dns_scope_udp_dns_socket(t->scope, &server);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
r = sd_event_add_io(t->scope->manager->event, &t->dns_event_source, fd, EPOLLIN, on_dns_packet, t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t->dns_fd = fd;
|
||||
fd = -1;
|
||||
t->server = dns_server_ref(server);
|
||||
}
|
||||
|
||||
r = dns_scope_emit(t->scope, t->dns_fd, t->sent);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
|
||||
DnsTransaction *t = userdata;
|
||||
int r;
|
||||
@@ -439,7 +479,7 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
|
||||
assert(t);
|
||||
|
||||
/* Timeout reached? Try again, with a new server */
|
||||
dns_scope_next_dns_server(t->scope);
|
||||
dns_transaction_next_dns_server(t);
|
||||
|
||||
r = dns_transaction_go(t);
|
||||
if (r < 0)
|
||||
@@ -516,7 +556,6 @@ int dns_transaction_go(DnsTransaction *t) {
|
||||
}
|
||||
|
||||
t->n_attempts++;
|
||||
t->server = dns_server_unref(t->server);
|
||||
t->received = dns_packet_unref(t->received);
|
||||
t->cached = dns_answer_unref(t->cached);
|
||||
t->cached_rcode = 0;
|
||||
@@ -596,13 +635,9 @@ int dns_transaction_go(DnsTransaction *t) {
|
||||
* always be made via TCP on LLMNR */
|
||||
r = dns_transaction_open_tcp(t);
|
||||
} else {
|
||||
DnsServer *server;
|
||||
|
||||
/* Try via UDP, and if that fails due to large size try via TCP */
|
||||
r = dns_scope_emit(t->scope, t, t->sent, &server);
|
||||
if (r >= 0)
|
||||
t->server = dns_server_ref(server);
|
||||
else if (r == -EMSGSIZE)
|
||||
r = dns_transaction_emit(t);
|
||||
if (r == -EMSGSIZE)
|
||||
r = dns_transaction_open_tcp(t);
|
||||
}
|
||||
if (r == -ESRCH) {
|
||||
@@ -616,7 +651,7 @@ int dns_transaction_go(DnsTransaction *t) {
|
||||
}
|
||||
|
||||
/* Couldn't send? Try immediately again, with a new server */
|
||||
dns_scope_next_dns_server(t->scope);
|
||||
dns_transaction_next_dns_server(t);
|
||||
|
||||
return dns_transaction_go(t);
|
||||
}
|
||||
@@ -634,91 +669,6 @@ int dns_transaction_go(DnsTransaction *t) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
DnsTransaction *t = userdata;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
assert(t->scope);
|
||||
|
||||
r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (dns_packet_validate_reply(p) > 0 &&
|
||||
DNS_PACKET_ID(p) == t->id) {
|
||||
dns_transaction_process_reply(t, p);
|
||||
} else
|
||||
log_debug("Invalid DNS packet.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int transaction_dns_ipv4_fd(DnsTransaction *t) {
|
||||
const int one = 1;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
assert(t->scope);
|
||||
assert(t->scope->manager);
|
||||
|
||||
if (t->dns_ipv4_fd >= 0)
|
||||
return t->dns_ipv4_fd;
|
||||
|
||||
t->dns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
|
||||
if (t->dns_ipv4_fd < 0)
|
||||
return -errno;
|
||||
|
||||
r = setsockopt(t->dns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
|
||||
if (r < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = sd_event_add_io(t->scope->manager->event, &t->dns_ipv4_event_source, t->dns_ipv4_fd, EPOLLIN, on_dns_packet, t);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
return t->dns_ipv4_fd;
|
||||
|
||||
fail:
|
||||
t->dns_ipv4_fd = safe_close(t->dns_ipv4_fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
int transaction_dns_ipv6_fd(DnsTransaction *t) {
|
||||
const int one = 1;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
assert(t->scope);
|
||||
assert(t->scope->manager);
|
||||
|
||||
if (t->dns_ipv6_fd >= 0)
|
||||
return t->dns_ipv6_fd;
|
||||
|
||||
t->dns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
|
||||
if (t->dns_ipv6_fd < 0)
|
||||
return -errno;
|
||||
|
||||
r = setsockopt(t->dns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
|
||||
if (r < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = sd_event_add_io(t->scope->manager->event, &t->dns_ipv6_event_source, t->dns_ipv6_fd, EPOLLIN, on_dns_packet, t);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
return t->dns_ipv6_fd;
|
||||
|
||||
fail:
|
||||
t->dns_ipv6_fd = safe_close(t->dns_ipv6_fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = {
|
||||
[DNS_TRANSACTION_NULL] = "null",
|
||||
[DNS_TRANSACTION_PENDING] = "pending",
|
||||
|
||||
@@ -61,11 +61,8 @@ struct DnsTransaction {
|
||||
sd_event_source *timeout_event_source;
|
||||
unsigned n_attempts;
|
||||
|
||||
int dns_ipv4_fd;
|
||||
int dns_ipv6_fd;
|
||||
|
||||
sd_event_source *dns_ipv4_event_source;
|
||||
sd_event_source *dns_ipv6_event_source;
|
||||
int dns_fd;
|
||||
sd_event_source *dns_event_source;
|
||||
|
||||
/* the active server */
|
||||
DnsServer *server;
|
||||
@@ -95,9 +92,6 @@ int dns_transaction_go(DnsTransaction *t);
|
||||
void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p);
|
||||
void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state);
|
||||
|
||||
int transaction_dns_ipv4_fd(DnsTransaction *t);
|
||||
int transaction_dns_ipv6_fd(DnsTransaction *t);
|
||||
|
||||
const char* dns_transaction_state_to_string(DnsTransactionState p) _const_;
|
||||
DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_;
|
||||
|
||||
|
||||
@@ -912,10 +912,12 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
|
||||
if (p->ifindex == LOOPBACK_IFINDEX)
|
||||
p->ifindex = 0;
|
||||
|
||||
/* If we don't know the interface index still, we look for the
|
||||
* first local interface with a matching address. Yuck! */
|
||||
if (p->ifindex <= 0)
|
||||
p->ifindex = manager_find_ifindex(m, p->family, &p->destination);
|
||||
if (protocol != DNS_PROTOCOL_DNS) {
|
||||
/* If we don't know the interface index still, we look for the
|
||||
* first local interface with a matching address. Yuck! */
|
||||
if (p->ifindex <= 0)
|
||||
p->ifindex = manager_find_ifindex(m, p->family, &p->destination);
|
||||
}
|
||||
|
||||
*ret = p;
|
||||
p = NULL;
|
||||
@@ -947,6 +949,42 @@ static int sendmsg_loop(int fd, struct msghdr *mh, int flags) {
|
||||
}
|
||||
}
|
||||
|
||||
static int write_loop(int fd, void *message, size_t length) {
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(message);
|
||||
|
||||
for (;;) {
|
||||
if (write(fd, message, length) >= 0)
|
||||
return 0;
|
||||
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
if (errno != EAGAIN)
|
||||
return -errno;
|
||||
|
||||
r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
int manager_write(Manager *m, int fd, DnsPacket *p) {
|
||||
int r;
|
||||
|
||||
log_debug("Sending %s packet with id %u", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p));
|
||||
|
||||
r = write_loop(fd, DNS_PACKET_DATA(p), p->size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_addr *addr, uint16_t port, DnsPacket *p) {
|
||||
union sockaddr_union sa = {
|
||||
.in.sin_family = AF_INET,
|
||||
|
||||
@@ -119,6 +119,7 @@ void manager_next_dns_server(Manager *m);
|
||||
|
||||
uint32_t manager_find_mtu(Manager *m);
|
||||
|
||||
int manager_write(Manager *m, int fd, DnsPacket *p);
|
||||
int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p);
|
||||
int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user