belle_sip_resolver.c 10.9 KB
Newer Older
Simon Morlat's avatar
Simon Morlat committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
	belle-sip - SIP (RFC3261) library.
    Copyright (C) 2010  Belledonne Communications SARL

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "belle_sip_resolver.h"

21 22 23 24 25 26 27
#include <stdlib.h>


#define DNS_EAGAIN  EAGAIN


static struct dns_resolv_conf *resconf(belle_sip_resolver_context_t *ctx) {
28
#ifndef _WIN32
29
	const char *path;
30
#endif
31 32 33 34 35 36 37 38 39 40
	int error;

	if (ctx->resconf)
		return ctx->resconf;

	if (!(ctx->resconf = dns_resconf_open(&error))) {
		belle_sip_error("%s dns_resconf_open error: %s", __FUNCTION__, dns_strerror(error));
		return NULL;
	}

41 42 43
#ifdef _WIN32
	error = dns_resconf_loadwin(ctx->resconf);
#else
44 45 46 47 48 49 50 51 52 53
	path = "/etc/resolv.conf";
	error = dns_resconf_loadpath(ctx->resconf, path);
	if (error) {
		belle_sip_error("%s dns_resconf_loadpath error [%s]: %s", __FUNCTION__, path, dns_strerror(error));
		return NULL;
	}

	path = "/etc/nsswitch.conf";
	error = dns_nssconf_loadpath(ctx->resconf, path);
	if (error) {
54
		belle_sip_message("%s dns_nssconf_loadpath error [%s]: %s", __FUNCTION__, path, dns_strerror(error));
55
	}
56
#endif
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

	return ctx->resconf;
}

static struct dns_hosts *hosts(belle_sip_resolver_context_t *ctx) {
	int error;

	if (ctx->hosts)
		return ctx->hosts;

	if (!(ctx->hosts = dns_hosts_local(&error))) {
		belle_sip_error("%s dns_hosts_local error: %s", __FUNCTION__, dns_strerror(error));
		return NULL;
	}

	return ctx->hosts;
}

struct dns_cache *cache(belle_sip_resolver_context_t *ctx) {
	return NULL;
}

static int resolver_process_a_data(belle_sip_resolver_context_t *ctx, unsigned int revents) {
80 81
	char host[NI_MAXHOST + 1];
	char service[NI_MAXSERV + 1];
82 83 84
	struct dns_packet *ans;
	struct dns_rr_i *I;
	int error;
85 86 87
#ifndef HAVE_C99
	struct dns_rr_i dns_rr_it;
#endif
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

	if (revents & BELLE_SIP_EVENT_TIMEOUT) {
		belle_sip_error("%s timed-out", __FUNCTION__);
		return BELLE_SIP_STOP;
	}
	if (ctx->cancelled) {
		return BELLE_SIP_STOP;
	}

	error = dns_res_check(ctx->R);
	if (!error) {
		struct dns_rr rr;
		union dns_any any;
		enum dns_section section = DNS_S_AN;

		ans = dns_res_fetch(ctx->R, &error);
104
#ifdef HAVE_C99
105
		I = dns_rr_i_new(ans, .section = 0);
106 107 108 109
#else
		memset(&dns_rr_it, 0, sizeof dns_rr_it);
		I = dns_rr_i_init(&dns_rr_it, ans);
#endif
110 111 112 113 114 115 116
		while (dns_rr_grep(&rr, 1, I, ans, &error)) {
			if (rr.section == section) {
				if ((error = dns_any_parse(dns_any_init(&any, sizeof(any)), &rr, ans))) {
					belle_sip_error("%s dns_any_parse error: %s", __FUNCTION__, dns_strerror(error));
					free(ans);
					return BELLE_SIP_STOP;
				}
117 118
				if ((ctx->family == AF_INET6) && (rr.class == DNS_C_IN) && (rr.type == DNS_T_AAAA)) {
					struct dns_aaaa *aaaa = &any.aaaa;
119 120 121 122 123 124 125 126
					struct sockaddr_in6 sin6;
					memset(&sin6, 0, sizeof(sin6));
					memcpy(&sin6.sin6_addr, &aaaa->addr, sizeof(sin6.sin6_addr));
					sin6.sin6_family = AF_INET6;
					sin6.sin6_port = ctx->port;
					if (getnameinfo((struct sockaddr *)&sin6, sizeof(sin6), host, sizeof(host), service, sizeof(service), NI_NUMERICHOST) != 0)
						continue;
					ctx->ai = belle_sip_ip_address_to_addrinfo(host, ctx->port);
127 128
					belle_sip_message("%s has address %s", ctx->name, host);
					break;
129 130 131
				} else {
					if ((rr.class == DNS_C_IN) && (rr.type == DNS_T_A)) {
						struct dns_a *a = &any.a;
132 133 134 135 136 137 138 139
						struct sockaddr_in sin;
						memset(&sin, 0, sizeof(sin));
						memcpy(&sin.sin_addr, &a->addr, sizeof(sin.sin_addr));
						sin.sin_family = AF_INET;
						sin.sin_port = ctx->port;
						if (getnameinfo((struct sockaddr *)&sin, sizeof(sin), host, sizeof(host), service, sizeof(service), NI_NUMERICHOST) != 0)
							continue;
						ctx->ai = belle_sip_ip_address_to_addrinfo(host, ctx->port);
140 141 142
						belle_sip_message("%s has address %s", ctx->name, host);
						break;
					}
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
				}
			}
		}
		free(ans);
		ctx->cb(ctx->cb_data, ctx->name, ctx->ai);
		return BELLE_SIP_STOP;
	}
	if (error != DNS_EAGAIN) {
		belle_sip_error("%s dns_res_check error: %s (%d)", __FUNCTION__, dns_strerror(error), error);
		return BELLE_SIP_STOP;
	}

	dns_res_poll(ctx->R, 0);
	return BELLE_SIP_CONTINUE;
}

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
static int _resolver_start_query(belle_sip_resolver_context_t *ctx, belle_sip_source_func_t datafunc, enum dns_type type, int timeout) {
	int error;

	if (!ctx->stack->send_error) {
		error = dns_res_submit(ctx->R, ctx->name, type, DNS_C_IN);
		if (error)
			belle_sip_error("%s dns_res_submit error [%s]: %s", __FUNCTION__, ctx->name, dns_strerror(error));
	} else {
		/* Error simulation */
		error = ctx->stack->send_error;
		belle_sip_error("%s dns_res_submit error [%s]: simulated error %d", __FUNCTION__, ctx->name, error);
	}
	if (error < 0) {
		return -1;
	}

	(*datafunc)(ctx, 0);
	belle_sip_socket_source_init((belle_sip_source_t*)ctx, datafunc, ctx, dns_res_pollfd(ctx->R), BELLE_SIP_EVENT_READ | BELLE_SIP_EVENT_TIMEOUT, timeout);
	return 0;
}

typedef struct delayed_send {
	belle_sip_resolver_context_t *ctx;
	belle_sip_source_func_t datafunc;
	enum dns_type type;
	int timeout;
} delayed_send_t;

static int on_delayed_send_do(delayed_send_t *ds) {
	belle_sip_message("%s sending now", __FUNCTION__);
	_resolver_start_query(ds->ctx, ds->datafunc, ds->type, ds->timeout);
	belle_sip_object_unref(ds->ctx);
	belle_sip_free(ds);
	return FALSE;
}

195 196
static int resolver_start_query(belle_sip_resolver_context_t *ctx, belle_sip_source_func_t datafunc, enum dns_type type, int timeout) {
	struct dns_hints *(*hints)() = &dns_hints_local;
197 198 199 200
	struct dns_options *opts;
#ifndef HAVE_C99
	struct dns_options opts_st;
#endif
201 202 203 204 205 206 207 208 209 210 211
	int error;

	if (!ctx->name) return -1;

	if (resconf(ctx))
		resconf(ctx)->options.recurse = 0;
	else
		return -1;
	if (!hosts(ctx))
		return -1;

212 213 214 215 216 217 218
#ifdef HAVE_C99
	opts = dns_opts();
#else
	memset(&opts_st, 0, sizeof opts_st);
	opts = &opts_st;
#endif
	if (!(ctx->R = dns_res_open(ctx->resconf, ctx->hosts, dns_hints_mortal(hints(ctx->resconf, &error)), cache(ctx), opts, &error))) {
219 220 221 222
		belle_sip_error("%s dns_res_open error [%s]: %s", __FUNCTION__, ctx->name, dns_strerror(error));
		return -1;
	}

223 224 225 226 227 228 229 230 231 232 233 234
	if (ctx->stack->tx_delay > 0) {
		delayed_send_t *ds = belle_sip_new(delayed_send_t);
		ds->ctx = (belle_sip_resolver_context_t *)belle_sip_object_ref(ctx);
		ds->datafunc = datafunc;
		ds->type = type;
		ds->timeout = timeout;
		belle_sip_main_loop_add_timeout(ctx->stack->ml, (belle_sip_source_func_t)on_delayed_send_do, ds, ctx->stack->tx_delay);
		belle_sip_socket_source_init((belle_sip_source_t*)ctx, datafunc, ctx, dns_res_pollfd(ctx->R), BELLE_SIP_EVENT_READ | BELLE_SIP_EVENT_TIMEOUT, timeout);
		belle_sip_message("%s DNS resolution delayed by %d ms", __FUNCTION__, ctx->stack->tx_delay);
		return 0;
	} else {
		return _resolver_start_query(ctx, datafunc, type, timeout);
235 236 237 238
	}
}


Simon Morlat's avatar
Simon Morlat committed
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267

int belle_sip_addrinfo_to_ip(const struct addrinfo *ai, char *ip, size_t ip_size, int *port){
	char serv[16];
	int err=getnameinfo(ai->ai_addr,ai->ai_addrlen,ip,ip_size,serv,sizeof(serv),NI_NUMERICHOST|NI_NUMERICSERV);
	if (err!=0){
		belle_sip_error("getnameinfo() error: %s",gai_strerror(err));
		strncpy(ip,"<bug!!>",ip_size);
	}
	if (port) *port=atoi(serv);
	return 0;
}

struct addrinfo * belle_sip_ip_address_to_addrinfo(const char *ipaddress, int port){
	struct addrinfo *res=NULL;
	struct addrinfo hints={0};
	char serv[10];
	int err;

	snprintf(serv,sizeof(serv),"%i",port);
	hints.ai_family=AF_UNSPEC;
	hints.ai_flags=AI_NUMERICSERV|AI_NUMERICHOST;
	err=getaddrinfo(ipaddress,serv,&hints,&res);
	if (err!=0){
		return NULL;
	}
	return res;
}


268
static void belle_sip_resolver_context_destroy(belle_sip_resolver_context_t *ctx){
269 270
	/* Do not free ctx->ai with freeaddrinfo(). Let the caller do it, otherwise
	   it will not be able to use it after the resolver has been destroyed. */
Simon Morlat's avatar
Simon Morlat committed
271 272
	if (ctx->name)
		belle_sip_free(ctx->name);
273 274 275 276 277 278
	if (ctx->R)
		dns_res_close(ctx->R);
	if (ctx->hosts)
		free(ctx->hosts);
	if (ctx->resconf)
		free(ctx->resconf);
Simon Morlat's avatar
Simon Morlat committed
279 280
}

281
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_resolver_context_t);
Simon Morlat's avatar
Simon Morlat committed
282
BELLE_SIP_INSTANCIATE_VPTR(belle_sip_resolver_context_t, belle_sip_source_t,belle_sip_resolver_context_destroy, NULL, NULL,FALSE);
Simon Morlat's avatar
Simon Morlat committed
283

284 285 286 287 288
unsigned long belle_sip_resolve(belle_sip_stack_t *stack, const char *name, int port, int family, belle_sip_resolver_callback_t cb , void *data, belle_sip_main_loop_t *ml) {
	struct addrinfo *res = belle_sip_ip_address_to_addrinfo(name, port);
	if (res == NULL) {
		/* Then perform asynchronous DNS query */
		belle_sip_resolver_context_t *ctx = belle_sip_object_new(belle_sip_resolver_context_t);
289
		ctx->stack = stack;
290 291 292 293 294 295 296
		ctx->cb_data = data;
		ctx->cb = cb;
		ctx->name = belle_sip_strdup(name);
		ctx->port = port;
		ctx->ai = NULL;
		if (family == 0) family = AF_UNSPEC;
		ctx->family = family;
297 298 299 300
		if (resolver_start_query(ctx,
				(belle_sip_source_func_t)resolver_process_a_data,
				(ctx->family == AF_INET6) ? DNS_T_AAAA : DNS_T_A,
				belle_sip_stack_get_dns_timeout(stack)) < 0) {
301 302 303
			belle_sip_object_unref(ctx);
			return 0;
		}
304

Ghislain MARY's avatar
Ghislain MARY committed
305
		/* The resolver context must never be removed manually from the main loop */
306
		belle_sip_main_loop_add_source(ml,(belle_sip_source_t*)ctx);
Ghislain MARY's avatar
Ghislain MARY committed
307
		belle_sip_object_unref(ctx);	/* The main loop has a ref on it */
308
		return ctx->source.id;
309 310
	} else {
		cb(data, name, res);
311 312
		return 0;
	}
Simon Morlat's avatar
Simon Morlat committed
313 314
}

315 316 317 318 319 320 321 322 323 324
void belle_sip_resolve_cancel(belle_sip_main_loop_t *ml, unsigned long id){
	if (id!=0){
		belle_sip_source_t *s=belle_sip_main_loop_find_source(ml,id);
		if (s){
			belle_sip_resolver_context_t *res=BELLE_SIP_RESOLVER_CONTEXT(s);
			res->cancelled=1;
		}
	}
}

325
void belle_sip_get_src_addr_for(const struct sockaddr *dest, socklen_t destlen, struct sockaddr *src, socklen_t *srclen){
326
	int af_type=dest->sa_family;
327 328
	int sock=socket(af_type,SOCK_DGRAM,IPPROTO_UDP);
	
329
	if (sock==(belle_sip_socket_t)-1){
330 331
		belle_sip_fatal("Could not create socket: %s",belle_sip_get_socket_error_string());
		goto fail;
332 333
	}
	if (connect(sock,dest,destlen)==-1){
334 335
		belle_sip_error("belle_sip_get_src_addr_for: connect() failed: %s",belle_sip_get_socket_error_string());
		goto fail;
336 337
	}
	if (getsockname(sock,src,srclen)==-1){
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
		belle_sip_error("belle_sip_get_src_addr_for: getsockname() failed: %s",belle_sip_get_socket_error_string());
		goto fail;
	}
	close_socket(sock);
	return;
fail:
	{
		struct addrinfo hints={0},*res=NULL;
		int err;
		hints.ai_family=af_type;
		err=getaddrinfo(af_type==AF_INET ? "0.0.0.0" : "::0","0",&hints,&res);
		if (err!=0) belle_sip_fatal("belle_sip_get_src_addr_for(): getaddrinfo failed: %s",belle_sip_get_socket_error_string_from_code(err));
		memcpy(src,res->ai_addr,MIN(*srclen,res->ai_addrlen));
		*srclen=res->ai_addrlen;
		freeaddrinfo(res);
353
	}
354
	if (sock==(belle_sip_socket_t)-1) close_socket(sock);
355
}