sock : Add DNS-SRV list sorted by priority and weight, check result connection.

This commit is contained in:
Chih-Wei Chen
2016-07-18 17:34:26 +08:00
committed by Dmitry Podgorny
parent f46cf669a3
commit 171b285cbe
3 changed files with 152 additions and 47 deletions

View File

@@ -426,11 +426,11 @@ int xmpp_connect_client(xmpp_conn_t * const conn,
xmpp_conn_handler callback, xmpp_conn_handler callback,
void * const userdata) void * const userdata)
{ {
char *buf = NULL; resolver_srv_rr_t *srv_rr_list = NULL;
char *domain; char *domain;
const char *host; const char *host;
unsigned short port; unsigned short port;
int found = 0; int found = XMPP_DOMAIN_NOT_FOUND;
int rc; int rc;
domain = xmpp_jid_domain(conn->ctx, conn->jid); domain = xmpp_jid_domain(conn->ctx, conn->jid);
@@ -440,19 +440,39 @@ int xmpp_connect_client(xmpp_conn_t * const conn,
xmpp_debug(conn->ctx, "xmpp", "Connecting via altdomain."); xmpp_debug(conn->ctx, "xmpp", "Connecting via altdomain.");
host = altdomain; host = altdomain;
port = altport ? altport : _conn_default_port(conn, XMPP_CLIENT); port = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
found = 1; found = XMPP_DOMAIN_ALTDOMAIN;
/* SSL tunneled connection on 5223 port is legacy and doesn't /* SSL tunneled connection on 5223 port is legacy and doesn't
* have an SRV record. */ * have an SRV record. */
} else if (!conn->tls_legacy_ssl) { } else if (!conn->tls_legacy_ssl) {
host = buf = xmpp_alloc(conn->ctx, MAX_DOMAIN_LEN); host = xmpp_alloc(conn->ctx, MAX_DOMAIN_LEN);
if (buf != NULL) { srv_rr_list = xmpp_alloc(conn->ctx, sizeof(resolver_srv_rr_t));
found = resolver_srv_lookup("xmpp-client", "tcp", domain, if (srv_rr_list != NULL) {
buf, MAX_DOMAIN_LEN, &port); srv_rr_list->next = NULL;
found = resolver_srv_lookup(conn->ctx, "xmpp-client", "tcp", domain,
&srv_rr_list);
/* Try DNS-SRV list connection*/
if (found == XMPP_DOMAIN_FOUND) {
resolver_srv_rr_t *srv_rr_p = srv_rr_list;
sock_t sock_try;
while(srv_rr_p != NULL) {
xmpp_debug(conn->ctx, "xmpp", "Try sock_connect %s:%d ",srv_rr_p->target, srv_rr_p->port);
sock_try = sock_connect(srv_rr_p->target, srv_rr_p->port);
if (sock_try == 0){
host = srv_rr_p->target;
port = srv_rr_p->port;
found = XMPP_DOMAIN_FOUND;
break;
}
srv_rr_p = srv_rr_p->next;
found = XMPP_DOMAIN_NOT_FOUND;
}
}
} }
} }
if (!found) { if (XMPP_DOMAIN_NOT_FOUND == found) {
xmpp_debug(conn->ctx, "xmpp", "SRV lookup failed, " xmpp_debug(conn->ctx, "xmpp", "SRV lookup failed, "
"connecting via domain."); "connecting via domain.");
host = domain; host = domain;
@@ -462,7 +482,11 @@ int xmpp_connect_client(xmpp_conn_t * const conn,
rc = _conn_connect(conn, domain, host, port, XMPP_CLIENT, rc = _conn_connect(conn, domain, host, port, XMPP_CLIENT,
callback, userdata); callback, userdata);
xmpp_free(conn->ctx, domain); xmpp_free(conn->ctx, domain);
if (buf) xmpp_free(conn->ctx, buf); while (srv_rr_list!=NULL) {
resolver_srv_rr_t *rr_next = srv_rr_list->next;
xmpp_free(conn->ctx, srv_rr_list);
srv_rr_list = rr_next;
}
return rc; return rc;
} }

View File

@@ -111,23 +111,91 @@ static unsigned message_name_len(const unsigned char *buf, size_t buf_len,
return message_name_get(buf, buf_len, buf_offset, NULL, SIZE_MAX); return message_name_get(buf, buf_len, buf_offset, NULL, SIZE_MAX);
} }
int resolver_srv_lookup_buf(const unsigned char *buf, size_t len, int resolver_srv_list_sort(resolver_srv_rr_t **srv_rr_list)
char *target, size_t target_len,
unsigned short *port)
{ {
int set = 0; resolver_srv_rr_t * rr_head;
resolver_srv_rr_t * rr_current;
resolver_srv_rr_t * rr_next;
resolver_srv_rr_t * rr_prev;
int swap;
rr_head = *srv_rr_list;
if ((rr_head == NULL) || (rr_head->next == NULL)) {
/* Empty or single record list */
return 0;
}
do {
rr_prev = NULL;
rr_current = rr_head;
rr_next = rr_head->next;
swap = 0;
while (rr_next!=NULL) {
/*
* RFC2052: When selecting a target host among the
* those that have the same priority, the chance of trying
* this one first SHOULD be proportional to its weight.
*/
if ((rr_current->priority > rr_next->priority) ||
(rr_current->priority == rr_next->priority && rr_current->weight < rr_next->weight ))
{
/* Swap node */
swap = 1;
if (rr_prev!=NULL) {
rr_prev->next = rr_next;
} else {
/* Swap head node */
rr_head = rr_next;
}
rr_current->next = rr_next->next;
rr_next->next = rr_current;
rr_prev = rr_next;
rr_next = rr_current->next;
} else {
/* Next node */
rr_prev = rr_current;
rr_current = rr_current->next;
rr_next = rr_current->next;
}
}
}while (swap != 0) ;
*srv_rr_list = rr_head;
return 0;
}
int resolver_srv_list_add(resolver_srv_rr_t **srv_rr_list, resolver_srv_rr_t *srv_rr_data)
{
resolver_srv_rr_t *rr_head;
rr_head = *srv_rr_list;
if (strlen(rr_head->target) > 0) {
srv_rr_data->next = rr_head;
}
*srv_rr_list = srv_rr_data;
return 0;
}
int resolver_srv_lookup_buf(xmpp_ctx_t *ctx, const unsigned char *buf, size_t len,
resolver_srv_rr_t **srv_rr_list)
{
int set = XMPP_DOMAIN_NOT_FOUND;
unsigned i; unsigned i;
unsigned j; unsigned j;
unsigned name_len; unsigned name_len;
unsigned rdlength; unsigned rdlength;
uint16_t type; uint16_t type;
uint16_t class; uint16_t class;
uint16_t priority;
uint16_t priority_min;
struct message_header header; struct message_header header;
resolver_srv_rr_t *srv_rr_data;
if (len < MESSAGE_HEADER_LEN) if (len < MESSAGE_HEADER_LEN)
return 0; return XMPP_DOMAIN_NOT_FOUND;
header.id = xmpp_ntohs_ptr(&buf[0]); header.id = xmpp_ntohs_ptr(&buf[0]);
header.octet2 = buf[2]; header.octet2 = buf[2];
@@ -139,7 +207,7 @@ int resolver_srv_lookup_buf(const unsigned char *buf, size_t len,
if (message_header_qr(&header) != MESSAGE_RESPONSE || if (message_header_qr(&header) != MESSAGE_RESPONSE ||
message_header_rcode(&header) != 0) message_header_rcode(&header) != 0)
{ {
return 0; return XMPP_DOMAIN_NOT_FOUND;
} }
j = MESSAGE_HEADER_LEN; j = MESSAGE_HEADER_LEN;
@@ -148,7 +216,7 @@ int resolver_srv_lookup_buf(const unsigned char *buf, size_t len,
name_len = message_name_len(buf, len, j); name_len = message_name_len(buf, len, j);
if (name_len == 0) { if (name_len == 0) {
/* error in name format */ /* error in name format */
return 0; return XMPP_DOMAIN_NOT_FOUND;
} }
j += name_len + 4; j += name_len + 4;
} }
@@ -157,7 +225,6 @@ int resolver_srv_lookup_buf(const unsigned char *buf, size_t len,
* RFC2052: A client MUST attempt to contact the target host * RFC2052: A client MUST attempt to contact the target host
* with the lowest-numbered priority it can reach. * with the lowest-numbered priority it can reach.
*/ */
priority_min = UINT16_MAX;
for (i = 0; i < header.ancount; ++i) { for (i = 0; i < header.ancount; ++i) {
name_len = message_name_len(buf, len, j); name_len = message_name_len(buf, len, j);
j += name_len; j += name_len;
@@ -166,49 +233,53 @@ int resolver_srv_lookup_buf(const unsigned char *buf, size_t len,
rdlength = xmpp_ntohs_ptr(&buf[j + 8]); rdlength = xmpp_ntohs_ptr(&buf[j + 8]);
j += 10; j += 10;
if (type == MESSAGE_T_SRV && class == MESSAGE_C_IN) { if (type == MESSAGE_T_SRV && class == MESSAGE_C_IN) {
priority = xmpp_ntohs_ptr(&buf[j]); srv_rr_data = xmpp_alloc(ctx, sizeof(*srv_rr_data));
if (!set || priority < priority_min) { srv_rr_data->priority = xmpp_ntohs_ptr(&buf[j]);
*port = xmpp_ntohs_ptr(&buf[j + 4]); srv_rr_data->weight = xmpp_ntohs_ptr(&buf[j+2]);
name_len = message_name_get(buf, len, j + 6, target, target_len); srv_rr_data->port = xmpp_ntohs_ptr(&buf[j + 4]);
set = name_len > 0 ? 1 : 0; name_len = message_name_get(buf, len, j + 6, &(srv_rr_data->target), MAX_DOMAIN_LEN);
priority_min = priority; srv_rr_data->next = NULL;
} set = name_len > 0 ? XMPP_DOMAIN_FOUND : XMPP_DOMAIN_NOT_FOUND;
resolver_srv_list_add(srv_rr_list,srv_rr_data);
} }
j += rdlength; j += rdlength;
} }
resolver_srv_list_sort(srv_rr_list);
return set; return set;
} }
int resolver_srv_lookup(const char *service, const char *proto, int resolver_srv_lookup(xmpp_ctx_t *ctx, const char *service, const char *proto,
const char *domain, char *target, const char *domain, resolver_srv_rr_t **srv_rr_list)
size_t target_len, unsigned short *port)
{ {
char fulldomain[2048]; char fulldomain[2048];
unsigned char buf[65535]; unsigned char buf[65535];
int len; int len;
int set = 0; int set = XMPP_DOMAIN_NOT_FOUND;
#ifdef _WIN32
resolver_srv_rr_t *rr;
#endif
xmpp_snprintf(fulldomain, sizeof(fulldomain), xmpp_snprintf(fulldomain, sizeof(fulldomain),
"_%s._%s.%s", service, proto, domain); "_%s._%s.%s", service, proto, domain);
#ifdef _WIN32 #ifdef _WIN32
set = resolver_win32_srv_lookup(fulldomain, target, target_len, port); rr = xmpp_alloc(ctx, sizeof(*rr));
if (set) rr->next = NULL;
return set; set = resolver_win32_srv_lookup(fulldomain, rr->target, sizeof(rr->target), &rr->port);
if (set) *srv_rr_list = rr;
else xmpp_free(ctx, rr);
len = resolver_win32_srv_query(fulldomain, buf, sizeof(buf)); len = resolver_win32_srv_query(fulldomain, buf, sizeof(buf));
#else /* _WIN32 */ #else /* _WIN32 */
len = res_query(fulldomain, MESSAGE_C_IN, MESSAGE_T_SRV, buf, sizeof(buf)); len = res_query(fulldomain, MESSAGE_C_IN, MESSAGE_T_SRV, buf, sizeof(buf));
#endif /* _WIN32 */ #endif /* _WIN32 */
if (len > 0) if (len > 0)
set = resolver_srv_lookup_buf(buf, (size_t)len, target, target_len, port); set = resolver_srv_lookup_buf(ctx, buf, (size_t)len, srv_rr_list);
return set; return set;
} }
/* FIXME: interface that returns array of results, maybe sorted by priority */
#ifdef _WIN32 #ifdef _WIN32
/******************************************************************************* /*******************************************************************************

View File

@@ -17,25 +17,35 @@
#define __LIBSTROPHE_RESOLVER_H__ #define __LIBSTROPHE_RESOLVER_H__
#include "ostypes.h" #include "ostypes.h"
#include "common.h"
/** Perform lookup for RFC1035 message format. */ typedef enum {
int resolver_srv_lookup_buf(const unsigned char *buf, size_t len, XMPP_DOMAIN_NOT_FOUND,
char *target, size_t target_len, XMPP_DOMAIN_FOUND,
unsigned short *port); XMPP_DOMAIN_ALTDOMAIN
} xmpp_domain_state_t;
typedef struct resolver_srv_rr_struc {
uint16_t priority;
uint16_t weight;
uint16_t port;
char target[MAX_DOMAIN_LEN];
struct resolver_srv_rr_struc *next;
}resolver_srv_rr_t;
/* This function allocates all elements including the 1st one. (*srv_rr_list) is the result */
int resolver_srv_lookup_buf(xmpp_ctx_t *ctx, const unsigned char *buf, size_t len,
resolver_srv_rr_t **srv_rr_list);
/** Resolve SRV record. /** Resolve SRV record.
* *
* @param service service of the SRV record * @param service service of the SRV record
* @param proto protocol of the SRV record * @param proto protocol of the SRV record
* @param domain resolving domain * @param domain resolving domain
* @param target pre-allocated string where result is stored * @param srv_rr_list is the result
* @param target_len maximum size of the target
* @param port pointer where resulting port is stored
* *
* @return 1 on success or 0 on fail * @return XMPP_DOMAIN_FOUND on success or XMPP_DOMAIN_NOT_FOUND on fail
*/ */
int resolver_srv_lookup(const char *service, const char *proto, int resolver_srv_lookup(xmpp_ctx_t *ctx, const char *service, const char *proto,
const char *domain, char *target, const char *domain, resolver_srv_rr_t **srv_rr_list);
size_t target_len, unsigned short *port);
#endif /* __LIBSTROPHE_RESOLVER_H__ */ #endif /* __LIBSTROPHE_RESOLVER_H__ */