summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordefanor <defanor@uberspace.net>2020-11-13 14:40:11 +0300
committerdefanor <defanor@uberspace.net>2020-11-13 14:40:11 +0300
commit3749774b44405f7cdafcd3bb13c7ecbcf34a2f26 (patch)
treea702921107ddb73126f31d63a9974daa9a35ef21
parent257999ac7a08789cc421983493e43ecf5e169bab (diff)
Switch from c-ares to libunbound
libunbound supports DNSSEC, which is needed for DANE TLSA: GnuTLS verifies a certificate for the final host, but SRV and A/AAAA records leading to it should be verified as well. c-ares is still used to parse domain names in SRV records, but should be replaced soon.
-rw-r--r--README3
-rw-r--r--configure.ac3
-rw-r--r--src/Makefile.am4
-rw-r--r--src/rexmpp.c263
-rw-r--r--src/rexmpp.h14
-rw-r--r--src/rexmpp_tcp.c104
-rw-r--r--src/rexmpp_tcp.h17
7 files changed, 248 insertions, 160 deletions
diff --git a/README b/README
index b03ddce..75f202d 100644
--- a/README
+++ b/README
@@ -14,7 +14,8 @@ rely on any particular UI, should be flexible and not stay in the way
of implementing additional XEPs on top of it, and should try to make
it easy to implement a decent client application using it.
-Current dependencies: c-ares, libxml2, gnutls, gnutls-dane, gsasl.
+Current dependencies: libunbound, c-ares, libxml2, gnutls,
+gnutls-dane, gsasl.
A rough roadmap:
diff --git a/configure.ac b/configure.ac
index 589fd4e..8fd21d6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -36,6 +36,9 @@ PKG_CHECK_MODULES([CARES], [libcares])
AC_SUBST(CARES_CFLAGS)
AC_SUBST(CARES_LIBS)
+PKG_CHECK_MODULES([UNBOUND], [libunbound])
+AC_SUBST(UNBOUND_CFLAGS)
+AC_SUBST(UNBOUND_LIBS)
# Checks for header files.
AC_CHECK_HEADERS([arpa/inet.h netdb.h netinet/in.h sys/socket.h syslog.h])
diff --git a/src/Makefile.am b/src/Makefile.am
index 182c79f..f3cd9c0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,5 +13,5 @@ librexmpp_la_SOURCES = rexmpp_roster.h rexmpp_roster.c \
rexmpp_socks.h rexmpp_socks.c \
rexmpp.h rexmpp.c
include_HEADERS = rexmpp_roster.h rexmpp_tcp.h rexmpp_socks.h rexmpp.h
-librexmpp_la_CFLAGS = $(AM_CFLAGS) $(LIBXML_CFLAGS) $(GNUTLS_CFLAGS) $(LIBDANE_CFLAGS) $(GSASL_CFLAGS) $(CARES_CFLAGS)
-librexmpp_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(LIBDANE_LIBS) $(GSASL_LIBS) $(CARES_LIBS)
+librexmpp_la_CFLAGS = $(AM_CFLAGS) $(LIBXML_CFLAGS) $(GNUTLS_CFLAGS) $(LIBDANE_CFLAGS) $(GSASL_CFLAGS) $(CARES_CFLAGS) $(UNBOUND_CFLAGS)
+librexmpp_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(LIBDANE_LIBS) $(GSASL_LIBS) $(CARES_LIBS) $(UNBOUND_LIBS)
diff --git a/src/rexmpp.c b/src/rexmpp.c
index 104d2b1..59059a2 100644
--- a/src/rexmpp.c
+++ b/src/rexmpp.c
@@ -20,6 +20,7 @@
#include <gnutls/x509.h>
#include <gnutls/dane.h>
#include <gsasl.h>
+#include <unbound.h>
#include "rexmpp.h"
#include "rexmpp_tcp.h"
@@ -242,10 +243,11 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, const char *jid)
s->track_roster_presence = 1;
s->send_buffer = NULL;
s->send_queue = NULL;
+ s->resolver_ctx = NULL;
s->server_srv = NULL;
- s->server_srv_cur = NULL;
+ s->server_srv_cur = -1;
s->server_srv_tls = NULL;
- s->server_srv_tls_cur = NULL;
+ s->server_srv_tls_cur = -1;
s->server_socket = -1;
s->current_element_root = NULL;
s->current_element = NULL;
@@ -292,19 +294,33 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, const char *jid)
return REXMPP_E_XML;
}
- err = ares_library_init(ARES_LIB_INIT_ALL);
- if (err != 0) {
- rexmpp_log(s, LOG_CRIT, "ares library initialisation error: %s",
- ares_strerror(err));
+ s->resolver_ctx = ub_ctx_create();
+ if (s->resolver_ctx == NULL) {
+ rexmpp_log(s, LOG_CRIT, "Failed to create resolver context");
xmlFreeParserCtxt(s->xml_parser);
return REXMPP_E_DNS;
}
+ err = ub_ctx_resolvconf(s->resolver_ctx, NULL);
+ if (err != 0) {
+ rexmpp_log(s, LOG_WARNING, "Failed to read resolv.conf: %s",
+ ub_strerror(err));
+ }
+ err = ub_ctx_hosts(s->resolver_ctx, NULL);
+ if (err != 0) {
+ rexmpp_log(s, LOG_WARNING, "Failed to read hosts file: %s",
+ ub_strerror(err));
+ }
+ /* todo: better to make this path configurable, not to hardcode it */
+ err = ub_ctx_trustedkeys(s->resolver_ctx, "/etc/unbound/root.key");
+ if (err != 0) {
+ rexmpp_log(s, LOG_WARNING, "Failed to set root key file for DNSSEC: %s",
+ ub_strerror(err));
+ }
- err = ares_init(&(s->resolver_channel));
- if (err) {
- rexmpp_log(s, LOG_CRIT, "ares channel initialisation error: %s",
+ err = ares_library_init(ARES_LIB_INIT_ALL);
+ if (err != 0) {
+ rexmpp_log(s, LOG_CRIT, "ares library initialisation error: %s",
ares_strerror(err));
- ares_library_cleanup();
xmlFreeParserCtxt(s->xml_parser);
return REXMPP_E_DNS;
}
@@ -313,7 +329,6 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, const char *jid)
if (err) {
rexmpp_log(s, LOG_CRIT, "gnutls credentials allocation error: %s",
gnutls_strerror(err));
- ares_destroy(s->resolver_channel);
ares_library_cleanup();
xmlFreeParserCtxt(s->xml_parser);
return REXMPP_E_TLS;
@@ -322,7 +337,6 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, const char *jid)
if (err < 0) {
rexmpp_log(s, LOG_CRIT, "Certificates loading error: %s",
gnutls_strerror(err));
- ares_destroy(s->resolver_channel);
ares_library_cleanup();
xmlFreeParserCtxt(s->xml_parser);
return REXMPP_E_TLS;
@@ -333,7 +347,6 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, const char *jid)
rexmpp_log(s, LOG_CRIT, "gsasl initialisation error: %s",
gsasl_strerror(err));
gnutls_certificate_free_credentials(s->gnutls_cred);
- ares_destroy(s->resolver_channel);
ares_library_cleanup();
xmlFreeParserCtxt(s->xml_parser);
return REXMPP_E_SASL;
@@ -395,14 +408,14 @@ void rexmpp_cleanup (rexmpp_t *s) {
s->input_queue_last = NULL;
}
if (s->server_srv != NULL) {
- ares_free_data(s->server_srv);
+ ub_resolve_free(s->server_srv);
s->server_srv = NULL;
- s->server_srv_cur = NULL;
+ s->server_srv_cur = -1;
}
if (s->server_srv_tls != NULL) {
- ares_free_data(s->server_srv_tls);
+ ub_resolve_free(s->server_srv_tls);
s->server_srv_tls = NULL;
- s->server_srv_tls_cur = NULL;
+ s->server_srv_tls_cur = -1;
}
s->sm_state = REXMPP_SM_INACTIVE;
s->ping_requested = 0;
@@ -413,7 +426,10 @@ void rexmpp_done (rexmpp_t *s) {
rexmpp_cleanup(s);
gsasl_done(s->sasl_ctx);
gnutls_certificate_free_credentials(s->gnutls_cred);
- ares_destroy(s->resolver_channel);
+ if (s->resolver_ctx != NULL) {
+ ub_ctx_delete(s->resolver_ctx);
+ s->resolver_ctx = NULL;
+ }
ares_library_cleanup();
xmlFreeParserCtxt(s->xml_parser);
if (s->initial_jid != NULL) {
@@ -942,6 +958,7 @@ void rexmpp_start_connecting (rexmpp_t *s) {
s->server_host, s->server_port);
rexmpp_process_conn_err(s,
rexmpp_tcp_conn_init(&s->server_connection,
+ s->resolver_ctx,
s->server_host,
s->server_port));
} else {
@@ -950,39 +967,44 @@ void rexmpp_start_connecting (rexmpp_t *s) {
s->socks_host, s->socks_port);
rexmpp_process_conn_err(s,
rexmpp_tcp_conn_init(&s->server_connection,
+ s->resolver_ctx,
s->socks_host,
s->socks_port));
}
}
void rexmpp_try_next_host (rexmpp_t *s) {
+ long enclen;
+ char *cur_data;
+ struct ub_result *cur_result;
+ int cur_number;
/* todo: check priorities and weights */
s->tls_state = REXMPP_TLS_INACTIVE;
- if (s->server_srv_tls != NULL && s->server_srv_tls_cur == NULL) {
+ if (s->server_srv_tls != NULL && s->server_srv_tls_cur == -1) {
/* We have xmpps-client records available, but haven't tried any
of them yet. */
- s->server_srv_tls_cur = s->server_srv_tls;
- s->server_host = s->server_srv_tls_cur->host;
- s->server_port = s->server_srv_tls_cur->port;
+ s->server_srv_tls_cur = 0;
+ cur_result = s->server_srv_tls;
+ cur_number = s->server_srv_tls_cur;
s->tls_state = REXMPP_TLS_AWAITING_DIRECT;
- } else if (s->server_srv_tls_cur != NULL &&
- s->server_srv_tls_cur->next != NULL) {
+ } else if (s->server_srv_tls_cur != -1 &&
+ s->server_srv_tls->data[s->server_srv_tls_cur + 1] != NULL) {
/* We have tried some xmpps-client records, but there is more. */
- s->server_srv_tls_cur = s->server_srv_tls_cur->next;
- s->server_host = s->server_srv_tls_cur->host;
- s->server_port = s->server_srv_tls_cur->port;
+ s->server_srv_tls_cur++;
+ cur_result = s->server_srv_tls;
+ cur_number = s->server_srv_tls_cur;
s->tls_state = REXMPP_TLS_AWAITING_DIRECT;
- } else if (s->server_srv != NULL && s->server_srv_cur == NULL) {
+ } else if (s->server_srv != NULL && s->server_srv_cur == -1) {
/* Starting with xmpp-client records. */
- s->server_srv_cur = s->server_srv;
- s->server_host = s->server_srv_cur->host;
- s->server_port = s->server_srv_cur->port;
- } else if (s->server_srv_tls_cur != NULL &&
- s->server_srv_tls_cur->next != NULL) {
+ s->server_srv_cur = 0;
+ cur_result = s->server_srv;
+ cur_number = s->server_srv_cur;
+ } else if (s->server_srv_cur != -1 &&
+ s->server_srv->data[s->server_srv_cur + 1] != NULL) {
/* Advancing in xmpp-client records. */
- s->server_srv_cur = s->server_srv_cur->next;
- s->server_host = s->server_srv_cur->host;
- s->server_port = s->server_srv_cur->port;
+ s->server_srv_cur++;
+ cur_result = s->server_srv;
+ cur_number = s->server_srv_cur;
} else {
/* No candidate records left to try. Schedule a reconnect. */
rexmpp_log(s, LOG_DEBUG,
@@ -991,6 +1013,21 @@ void rexmpp_try_next_host (rexmpp_t *s) {
rexmpp_schedule_reconnect(s);
return;
}
+ if (cur_result->len[cur_number] < 7) {
+ rexmpp_log(s, LOG_ERR, "An SRV record is too short.");
+ rexmpp_cleanup(s);
+ rexmpp_schedule_reconnect(s);
+ return;
+ }
+ cur_data = cur_result->data[cur_number];
+ /* TODO: replace the following with a custom function, to remove the
+ c-ares dependency. */
+ ares_expand_name(cur_data + 6,
+ cur_data,
+ cur_result->len[cur_number],
+ (char**)&(s->server_host),
+ &enclen);
+ s->server_port = cur_data[4] * 0x100 + cur_data[5];
rexmpp_start_connecting(s);
}
@@ -1002,29 +1039,48 @@ rexmpp_err_t rexmpp_tls_handshake (rexmpp_t *s) {
return REXMPP_E_AGAIN;
} else if (ret == 0) {
int status;
+
+ int srv_is_secure = 0;
+ if (s->stream_state == REXMPP_STREAM_NONE &&
+ s->server_srv_tls != NULL) { /* Direct TLS */
+ srv_is_secure = s->server_srv_tls->secure;
+ } else if (s->stream_state != REXMPP_STREAM_NONE &&
+ s->server_srv != NULL) { /* STARTTLS connection */
+ srv_is_secure = s->server_srv->secure;
+ }
+
/* Check DANE TLSA records; experimental and purely informative
now, but may be nice to (optionally) rely on it in the
future. */
- ret = dane_verify_session_crt(NULL, s->gnutls_session, s->server_host,
- "tcp", s->server_port, 0, 0, &status);
- if (ret) {
- rexmpp_log(s, LOG_WARNING, "DANE verification error: %s",
- dane_strerror(ret));
- } else if (status) {
- if (status & DANE_VERIFY_CA_CONSTRAINTS_VIOLATED) {
- rexmpp_log(s, LOG_WARNING, "The CA constraints were violated");
- }
- if (status & DANE_VERIFY_CERT_DIFFERS) {
- rexmpp_log(s, LOG_WARNING, "The certificate obtained via DNS differs");
- }
- if (status & DANE_VERIFY_UNKNOWN_DANE_INFO) {
- rexmpp_log(s, LOG_WARNING,
- "No known DANE data was found in the DNS record");
+ if ((srv_is_secure || s->manual_host != NULL) &&
+ s->server_socket_dns_secure) {
+ /* Apparently GnuTLS only checks against the target
+ server/derived host, while another possibility is a
+ service/source host
+ (<https://tools.ietf.org/html/rfc7712#section-5.1>,
+ <https://tools.ietf.org/html/rfc7673#section-6>). */
+ ret = dane_verify_session_crt(NULL, s->gnutls_session, s->server_host,
+ "tcp", s->server_port, 0, 0, &status);
+ if (ret) {
+ rexmpp_log(s, LOG_WARNING, "DANE verification error: %s",
+ dane_strerror(ret));
+ } else if (status) {
+ if (status & DANE_VERIFY_CA_CONSTRAINTS_VIOLATED) {
+ rexmpp_log(s, LOG_WARNING, "The CA constraints were violated");
+ }
+ if (status & DANE_VERIFY_CERT_DIFFERS) {
+ rexmpp_log(s, LOG_WARNING, "The certificate obtained via DNS differs");
+ }
+ if (status & DANE_VERIFY_UNKNOWN_DANE_INFO) {
+ rexmpp_log(s, LOG_WARNING,
+ "No known DANE data was found in the DNS record");
+ }
+ } else {
+ rexmpp_log(s, LOG_INFO,
+ "DANE verification did not reject the certificate");
}
- } else {
- rexmpp_log(s, LOG_INFO,
- "DANE verification did not reject the certificate");
}
+
ret = gnutls_certificate_verify_peers3(s->gnutls_session,
jid_bare_to_host(s->initial_jid),
&status);
@@ -1117,7 +1173,9 @@ rexmpp_err_t rexmpp_tls_start (rexmpp_t *s) {
rexmpp_err_t rexmpp_connected_to_server (rexmpp_t *s) {
s->tcp_state = REXMPP_TCP_CONNECTED;
- rexmpp_log(s, LOG_INFO, "Connected to the server");
+ rexmpp_log(s, LOG_INFO,
+ "Connected to the server, the used address record was %s",
+ s->server_socket_dns_secure ? "secure" : "not secure");
s->reconnect_number = 0;
xmlCtxtResetPush(s->xml_parser, "", 0, "", "utf-8");
if (s->tls_state == REXMPP_TLS_AWAITING_DIRECT) {
@@ -1142,6 +1200,7 @@ void rexmpp_process_socks_err (rexmpp_t *s, enum socks_err err) {
void rexmpp_process_conn_err (rexmpp_t *s, enum rexmpp_tcp_conn_error err) {
s->tcp_state = REXMPP_TCP_CONNECTING;
if (err == REXMPP_CONN_DONE) {
+ s->server_socket_dns_secure = s->server_connection.dns_secure;
s->server_socket = rexmpp_tcp_conn_finish(&s->server_connection);
if (s->socks_host == NULL) {
rexmpp_connected_to_server(s);
@@ -1163,7 +1222,34 @@ void rexmpp_process_conn_err (rexmpp_t *s, enum rexmpp_tcp_conn_error err) {
}
}
-void rexmpp_after_srv (rexmpp_t *s) {
+void rexmpp_srv_cb (void *s_ptr,
+ int err,
+ struct ub_result *result)
+{
+ rexmpp_t *s = s_ptr;
+ if (err == 0) {
+ if (result->bogus) {
+ rexmpp_log(s, LOG_WARNING,
+ "Received a bogus SRV resolution result for %s",
+ result->qname);
+ } else if (result->havedata) {
+ rexmpp_log(s,
+ result->secure ? LOG_DEBUG : LOG_WARNING,
+ "Resolved %s SRV record (%s)",
+ result->qname, result->secure ? "secure" : "not secure");
+ if (strncmp("_xmpp-", result->qname, 6) == 0) {
+ s->server_srv = result;
+ } else {
+ s->server_srv_tls = result;
+ }
+ } else {
+ rexmpp_log(s, LOG_DEBUG, "No data in the %s SRV result", result->qname);
+ }
+ } else {
+ rexmpp_log(s, LOG_WARNING, "Failed to query %s SRV records: %s",
+ result->qname, ub_strerror(err));
+ }
+
if (s->resolver_state == REXMPP_RESOLVER_SRV) {
s->resolver_state = REXMPP_RESOLVER_SRV_2;
} else if (s->resolver_state == REXMPP_RESOLVER_SRV_2) {
@@ -1185,43 +1271,6 @@ void rexmpp_after_srv (rexmpp_t *s) {
}
}
-void rexmpp_srv_tls_cb (void *s_ptr,
- int status,
- int timeouts,
- unsigned char *abuf,
- int alen)
-{
- rexmpp_t *s = s_ptr;
- if (status == ARES_SUCCESS) {
- ares_parse_srv_reply(abuf, alen, &(s->server_srv_tls));
- } else {
- rexmpp_log(s, LOG_WARNING, "Failed to query an xmpps-client SRV record: %s",
- ares_strerror(status));
- }
- if (status != ARES_EDESTRUCTION) {
- rexmpp_after_srv(s);
- }
-}
-
-void rexmpp_srv_cb (void *s_ptr,
- int status,
- int timeouts,
- unsigned char *abuf,
- int alen)
-{
- rexmpp_t *s = s_ptr;
- if (status == ARES_SUCCESS) {
- ares_parse_srv_reply(abuf, alen, &(s->server_srv));
- } else {
- rexmpp_log(s, LOG_WARNING, "Failed to query an xmpp-client SRV record: %s",
- ares_strerror(status));
- }
- if (status != ARES_EDESTRUCTION) {
- rexmpp_after_srv(s);
- }
-}
-
-
/* Should be called after reconnect, and after rexmpp_sm_handle_ack in
case of resumption. */
rexmpp_err_t rexmpp_resend_stanzas (rexmpp_t *s) {
@@ -1996,12 +2045,21 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
char *srv_query = malloc(srv_query_buf_len);
snprintf(srv_query, srv_query_buf_len,
"_xmpps-client._tcp.%s.", jid_bare_to_host(s->initial_jid));
- ares_query(s->resolver_channel, srv_query,
- ns_c_in, ns_t_srv, rexmpp_srv_tls_cb, s);
+ int err;
+ err = ub_resolve_async(s->resolver_ctx, srv_query, 33, 1,
+ s, rexmpp_srv_cb, NULL);
+ if (err) {
+ rexmpp_log(s, LOG_ERR, "Failed to query %s SRV record: %s",
+ srv_query, ub_strerror(err));
+ }
snprintf(srv_query, srv_query_buf_len,
"_xmpp-client._tcp.%s.", jid_bare_to_host(s->initial_jid));
- ares_query(s->resolver_channel, srv_query,
- ns_c_in, ns_t_srv, rexmpp_srv_cb, s);
+ err = ub_resolve_async(s->resolver_ctx, srv_query, 33, 1,
+ s, rexmpp_srv_cb, NULL);
+ if (err) {
+ rexmpp_log(s, LOG_ERR, "Failed to query %s SRV record: %s",
+ srv_query, ub_strerror(err));
+ }
s->resolver_state = REXMPP_RESOLVER_SRV;
free(srv_query);
} else {
@@ -2022,7 +2080,9 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
connection initiation. */
if (s->resolver_state != REXMPP_RESOLVER_NONE &&
s->resolver_state != REXMPP_RESOLVER_READY) {
- ares_process(s->resolver_channel, read_fds, write_fds);
+ if (ub_poll(s->resolver_ctx)) {
+ ub_process(s->resolver_ctx);
+ }
}
/* Connecting. Continues in rexmpp_process_conn_err, possibly
@@ -2121,7 +2181,10 @@ int rexmpp_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
if (s->resolver_state != REXMPP_RESOLVER_NONE &&
s->resolver_state != REXMPP_RESOLVER_READY) {
- max_fd = ares_fds(s->resolver_channel, read_fds, write_fds);
+ max_fd = ub_fd(s->resolver_ctx) + 1;
+ if (max_fd != 0) {
+ FD_SET(max_fd - 1, read_fds);
+ }
}
if (s->tcp_state == REXMPP_TCP_CONNECTING) {
@@ -2174,7 +2237,7 @@ struct timeval *rexmpp_timeout (rexmpp_t *s,
if (s->resolver_state != REXMPP_RESOLVER_NONE &&
s->resolver_state != REXMPP_RESOLVER_READY) {
- ret = ares_timeout(s->resolver_channel, max_tv, tv);
+
} else if (s->tcp_state == REXMPP_TCP_CONNECTING) {
ret = rexmpp_tcp_conn_timeout(&s->server_connection, max_tv, tv);
}
diff --git a/src/rexmpp.h b/src/rexmpp.h
index 2dfbde8..78ec5a6 100644
--- a/src/rexmpp.h
+++ b/src/rexmpp.h
@@ -10,6 +10,7 @@
#define REXMPP_H
#include <ares.h>
+#include <unbound.h>
#include <gnutls/gnutls.h>
#include <gsasl.h>
#include <libxml/tree.h>
@@ -282,11 +283,11 @@ struct rexmpp
time_t last_network_activity;
/* DNS-related structures. */
- ares_channel resolver_channel;
- struct ares_srv_reply *server_srv;
- struct ares_srv_reply *server_srv_cur;
- struct ares_srv_reply *server_srv_tls;
- struct ares_srv_reply *server_srv_tls_cur;
+ struct ub_ctx *resolver_ctx;
+ struct ub_result *server_srv;
+ int server_srv_cur;
+ struct ub_result *server_srv_tls;
+ int server_srv_tls_cur;
/* The XMPP server we are connecting to. */
const char *server_host;
@@ -294,6 +295,9 @@ struct rexmpp
/* The primary socket used for communication with the server. */
int server_socket;
+ /* Whether the address it's connected to was verified with
+ DNSSEC. */
+ int server_socket_dns_secure;
/* A structure used to establish a TCP connection. */
rexmpp_tcp_conn_t server_connection;
diff --git a/src/rexmpp_tcp.c b/src/rexmpp_tcp.c
index e57cfb8..ac32e57 100644
--- a/src/rexmpp_tcp.c
+++ b/src/rexmpp_tcp.c
@@ -6,7 +6,7 @@
@copyright MIT license.
*/
-#include <ares.h>
+#include <unbound.h>
#include <netdb.h>
#include <arpa/nameser.h>
#include <sys/socket.h>
@@ -24,15 +24,13 @@
void rexmpp_dns_aaaa_cb (void *ptr,
int status,
- int timeouts,
- unsigned char *abuf,
- int alen)
+ struct ub_result *result)
{
rexmpp_tcp_conn_t *conn = ptr;
conn->resolver_status_v6 = status;
- if (status == ARES_SUCCESS) {
+ conn->resolved_v6 = result;
+ if (status == 0 && ! result->bogus && result->havedata) {
conn->resolution_v6 = REXMPP_CONN_RESOLUTION_SUCCESS;
- ares_parse_aaaa_reply(abuf, alen, &(conn->addr_v6), NULL, NULL);
conn->addr_cur_v6 = -1;
} else {
conn->resolution_v6 = REXMPP_CONN_RESOLUTION_FAILURE;
@@ -41,15 +39,13 @@ void rexmpp_dns_aaaa_cb (void *ptr,
void rexmpp_dns_a_cb (void *ptr,
int status,
- int timeouts,
- unsigned char *abuf,
- int alen)
+ struct ub_result *result)
{
rexmpp_tcp_conn_t *conn = ptr;
conn->resolver_status_v4 = status;
- if (status == ARES_SUCCESS) {
+ conn->resolved_v4 = result;
+ if (status == 0 && ! result->bogus && result->havedata) {
conn->resolution_v4 = REXMPP_CONN_RESOLUTION_SUCCESS;
- ares_parse_a_reply(abuf, alen, &(conn->addr_v4), NULL, NULL);
conn->addr_cur_v4 = -1;
if (conn->resolution_v6 == REXMPP_CONN_RESOLUTION_WAITING) {
/* Wait for 50 ms for IPv6. */
@@ -74,22 +70,29 @@ void rexmpp_tcp_cleanup (rexmpp_tcp_conn_t *conn) {
}
}
if (conn->resolution_v4 != REXMPP_CONN_RESOLUTION_INACTIVE) {
- ares_destroy(conn->resolver_channel);
conn->resolution_v4 = REXMPP_CONN_RESOLUTION_INACTIVE;
conn->resolution_v6 = REXMPP_CONN_RESOLUTION_INACTIVE;
}
- if (conn->addr_v4 != NULL) {
- ares_free_hostent(conn->addr_v4);
- conn->addr_v4 = NULL;
+ if (conn->resolved_v4 != NULL) {
+ ub_resolve_free(conn->resolved_v4);
+ conn->resolved_v4 = NULL;
}
- if (conn->addr_v6 != NULL) {
- ares_free_hostent(conn->addr_v6);
- conn->addr_v6 = NULL;
+ if (conn->resolved_v6 != NULL) {
+ ub_resolve_free(conn->resolved_v6);
+ conn->resolved_v6 = NULL;
}
}
rexmpp_tcp_conn_error_t
rexmpp_tcp_connected (rexmpp_tcp_conn_t *conn, int fd) {
+ struct sockaddr sa;
+ socklen_t sa_len = sizeof(sa);
+ getsockname(fd, &sa, &sa_len);
+ if (sa.sa_family == AF_INET) {
+ conn->dns_secure = conn->resolved_v4->secure;
+ } else {
+ conn->dns_secure = conn->resolved_v6->secure;
+ }
conn->fd = fd;
rexmpp_tcp_cleanup(conn);
return REXMPP_CONN_DONE;
@@ -97,6 +100,7 @@ rexmpp_tcp_connected (rexmpp_tcp_conn_t *conn, int fd) {
rexmpp_tcp_conn_error_t
rexmpp_tcp_conn_init (rexmpp_tcp_conn_t *conn,
+ struct ub_ctx *resolver_ctx,
const char *host,
uint16_t port)
{
@@ -106,9 +110,10 @@ rexmpp_tcp_conn_init (rexmpp_tcp_conn_t *conn,
}
conn->connection_attempts = 0;
conn->port = port;
- conn->addr_v4 = NULL;
- conn->addr_v6 = NULL;
+ conn->resolved_v4 = NULL;
+ conn->resolved_v6 = NULL;
conn->fd = -1;
+ conn->dns_secure = 0;
conn->next_connection_time.tv_sec = 0;
conn->next_connection_time.tv_usec = 0;
@@ -160,15 +165,12 @@ rexmpp_tcp_conn_init (rexmpp_tcp_conn_t *conn,
}
conn->resolution_v4 = REXMPP_CONN_RESOLUTION_WAITING;
conn->resolution_v6 = REXMPP_CONN_RESOLUTION_WAITING;
- conn->resolver_error = ares_init(&(conn->resolver_channel));
- if (conn->resolver_error != ARES_SUCCESS) {
- return REXMPP_CONN_RESOLVER_ERROR;
- }
+ conn->resolver_ctx = resolver_ctx;
- ares_query(conn->resolver_channel, host,
- ns_c_in, ns_t_aaaa, rexmpp_dns_aaaa_cb, conn);
- ares_query(conn->resolver_channel, host,
- ns_c_in, ns_t_a, rexmpp_dns_a_cb, conn);
+ ub_resolve_async(conn->resolver_ctx, host, 28, 1,
+ conn, rexmpp_dns_aaaa_cb, NULL);
+ ub_resolve_async(conn->resolver_ctx, host, 1, 1,
+ conn, rexmpp_dns_a_cb, NULL);
return REXMPP_CONN_IN_PROGRESS;
}
@@ -180,14 +182,14 @@ int rexmpp_tcp_conn_finish (rexmpp_tcp_conn_t *conn) {
int rexmpp_tcp_conn_ipv4_available(rexmpp_tcp_conn_t *conn) {
return (conn->resolution_v4 == REXMPP_CONN_RESOLUTION_SUCCESS &&
- conn->addr_v4 != NULL &&
- conn->addr_v4->h_addr_list[conn->addr_cur_v4 + 1] != NULL);
+ conn->resolved_v4 != NULL &&
+ conn->resolved_v4->data[conn->addr_cur_v4 + 1] != NULL);
}
int rexmpp_tcp_conn_ipv6_available(rexmpp_tcp_conn_t *conn) {
return (conn->resolution_v6 == REXMPP_CONN_RESOLUTION_SUCCESS &&
- conn->addr_v6 != NULL &&
- conn->addr_v6->h_addr_list[conn->addr_cur_v6 + 1] != NULL);
+ conn->resolved_v6 != NULL &&
+ conn->resolved_v6->data[conn->addr_cur_v6 + 1] != NULL);
}
rexmpp_tcp_conn_error_t
@@ -219,7 +221,9 @@ rexmpp_tcp_conn_proceed (rexmpp_tcp_conn_t *conn,
/* Name resolution. */
if (conn->resolution_v4 == REXMPP_CONN_RESOLUTION_WAITING ||
conn->resolution_v6 == REXMPP_CONN_RESOLUTION_WAITING) {
- ares_process(conn->resolver_channel, read_fds, write_fds);
+ if (ub_poll(conn->resolver_ctx)) {
+ ub_process(conn->resolver_ctx);
+ }
}
if (conn->resolution_v4 == REXMPP_CONN_RESOLUTION_FAILURE &&
@@ -255,25 +259,34 @@ rexmpp_tcp_conn_proceed (rexmpp_tcp_conn_t *conn,
struct sockaddr *addr;
socklen_t addrlen;
int domain;
+ int len;
if (use_ipv6) {
conn->addr_cur_v6++;
+ len = sizeof(addr_v6.sin6_addr);
+ if (len > conn->resolved_v6->len[conn->addr_cur_v6]) {
+ len = conn->resolved_v6->len[conn->addr_cur_v6];
+ }
memcpy(&addr_v6.sin6_addr,
- conn->addr_v6->h_addr_list[conn->addr_cur_v6],
- conn->addr_v6->h_length);
- addr_v6.sin6_family = conn->addr_v6->h_addrtype;
+ conn->resolved_v6->data[conn->addr_cur_v6],
+ len);
+ addr_v6.sin6_family = AF_INET6;
addr_v6.sin6_port = htons(conn->port);
- domain = conn->addr_v6->h_addrtype;
+ domain = AF_INET6;
addr = (struct sockaddr*)&addr_v6;
addrlen = sizeof(addr_v6);
} else {
conn->addr_cur_v4++;
+ len = sizeof(addr_v4.sin_addr);
+ if (len > conn->resolved_v4->len[conn->addr_cur_v4]) {
+ len = conn->resolved_v4->len[conn->addr_cur_v4];
+ }
memcpy(&addr_v4.sin_addr,
- conn->addr_v4->h_addr_list[conn->addr_cur_v4],
- conn->addr_v4->h_length);
- addr_v4.sin_family = conn->addr_v4->h_addrtype;
+ conn->resolved_v4->data[conn->addr_cur_v4],
+ len);
+ addr_v4.sin_family = AF_INET;
addr_v4.sin_port = htons(conn->port);
- domain = conn->addr_v4->h_addrtype;
+ domain = AF_INET;
addr = (struct sockaddr*)&addr_v4;
addrlen = sizeof(addr_v4);
}
@@ -337,7 +350,10 @@ int rexmpp_tcp_conn_fds (rexmpp_tcp_conn_t *conn,
int max_fd = 0, i;
if (conn->resolution_v4 == REXMPP_CONN_RESOLUTION_WAITING ||
conn->resolution_v6 == REXMPP_CONN_RESOLUTION_WAITING) {
- max_fd = ares_fds(conn->resolver_channel, read_fds, write_fds);
+ max_fd = ub_fd(conn->resolver_ctx) + 1;
+ if (max_fd != 0) {
+ FD_SET(max_fd - 1, read_fds);
+ }
}
for (i = 0; i < REXMPP_TCP_MAX_CONNECTION_ATTEMPTS; i++) {
if (conn->sockets[i] != -1) {
@@ -356,10 +372,6 @@ struct timeval *rexmpp_tcp_conn_timeout (rexmpp_tcp_conn_t *conn,
{
struct timeval now;
struct timeval *ret = max_tv;
- if (conn->resolution_v4 == REXMPP_CONN_RESOLUTION_WAITING ||
- conn->resolution_v6 == REXMPP_CONN_RESOLUTION_WAITING) {
- ret = ares_timeout(conn->resolver_channel, max_tv, tv);
- }
if (conn->resolution_v4 == REXMPP_CONN_RESOLUTION_SUCCESS ||
conn->resolution_v6 == REXMPP_CONN_RESOLUTION_SUCCESS ||
(conn->resolution_v4 == REXMPP_CONN_RESOLUTION_INACTIVE &&
diff --git a/src/rexmpp_tcp.h b/src/rexmpp_tcp.h
index e4a6dff..c8cbdea 100644
--- a/src/rexmpp_tcp.h
+++ b/src/rexmpp_tcp.h
@@ -68,8 +68,8 @@ struct rexmpp_tcp_connection {
/** @brief A port we are connecting to. */
uint16_t port;
- /** @brief Resolver channel. */
- ares_channel resolver_channel;
+ /** @brief Resolver context. */
+ struct ub_ctx *resolver_ctx;
/** @brief Resolver error is stored here when
::REXMPP_CONN_RESOLVER_ERROR is returned. */
int resolver_error;
@@ -79,8 +79,8 @@ struct rexmpp_tcp_connection {
/** @brief Status of A record resolution, as returned by the
resolver. */
int resolver_status_v4;
- /** @brief AF_INET (IPv4) hostent structure. */
- struct hostent *addr_v4;
+ /** @brief Resolved A records. */
+ struct ub_result *resolved_v4;
/** @brief The AF_INET address number we are currently at. */
int addr_cur_v4;
@@ -89,8 +89,8 @@ struct rexmpp_tcp_connection {
/** @brief Status of AAAA record resolution, as returned by the
resolver. */
int resolver_status_v6;
- /** @brief AF_INET6 (IPv6) hostent structure. */
- struct hostent *addr_v6;
+ /** @brief Resolved AAAA records. */
+ struct ub_result *resolved_v6;
/** @brief The AF_INET6 address number we are currently at. */
int addr_cur_v6;
@@ -103,11 +103,15 @@ struct rexmpp_tcp_connection {
struct timeval next_connection_time;
/** @brief File descriptor of a connected socket. */
int fd;
+ /** @brief Whether the A or AAAA records used to establish the final
+ connection were verified with DNSSEC. */
+ int dns_secure;
};
/**
@brief Initiates a connection.
@param[out] conn An allocated connection structure.
+ @param[in] resolver_ctx Resolver context to use.
@param[in] host A host to connect to. This could be a domain name,
or a textual representation of an IPv4 or an IPv6 address.
@param[in] port A port to connect to.
@@ -115,6 +119,7 @@ struct rexmpp_tcp_connection {
*/
rexmpp_tcp_conn_error_t
rexmpp_tcp_conn_init (rexmpp_tcp_conn_t *conn,
+ struct ub_ctx *resolver_ctx,
const char *host,
uint16_t port);