belle_sip_resolver.c 6.67 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"

Simon Morlat's avatar
Simon Morlat committed
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

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;
}


50
static void belle_sip_resolver_context_destroy(belle_sip_resolver_context_t *ctx){
Simon Morlat's avatar
Simon Morlat committed
51
	if (ctx->thread!=0){
Simon Morlat's avatar
Simon Morlat committed
52
		belle_sip_thread_join(ctx->thread,NULL);
Simon Morlat's avatar
Simon Morlat committed
53 54 55 56 57 58
	}
	if (ctx->name)
		belle_sip_free(ctx->name);
	if (ctx->ai){
		freeaddrinfo(ctx->ai);
	}
Simon Morlat's avatar
Simon Morlat committed
59
#ifndef WIN32
Simon Morlat's avatar
Simon Morlat committed
60 61
	close(ctx->ctlpipe[0]);
	close(ctx->ctlpipe[1]);
62 63 64
#else
	if (ctx->ctlevent!=(belle_sip_fd_t)-1)
		CloseHandle(ctx->ctlevent);
Simon Morlat's avatar
Simon Morlat committed
65
#endif
Simon Morlat's avatar
Simon Morlat committed
66 67
}

68
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_resolver_context_t);
Simon Morlat's avatar
Simon Morlat committed
69
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
70

Simon Morlat's avatar
Simon Morlat committed
71
static int resolver_callback(belle_sip_resolver_context_t *ctx){
72
	belle_sip_message("resolver_callback() for %p (%s) called, done=%i, cancelled=%i",ctx->cb_data,ctx->name,(int)ctx->done,(int)ctx->cancelled);
73 74 75 76
	if (!ctx->cancelled){
		ctx->cb(ctx->cb_data, ctx->name, ctx->ai);
		ctx->ai=NULL;
	}
Simon Morlat's avatar
Simon Morlat committed
77
#ifndef WIN32
78 79 80 81 82
	{
		char tmp;
		if (read(ctx->source.fd,&tmp,1)!=1){
			belle_sip_fatal("Unexpected read from resolver_callback");
		}
Simon Morlat's avatar
Simon Morlat committed
83
	}
Simon Morlat's avatar
Simon Morlat committed
84
#endif
85
	/*by returning stop, we'll be removed from main loop and destroyed. */
Simon Morlat's avatar
Simon Morlat committed
86
	return BELLE_SIP_STOP;
Simon Morlat's avatar
Simon Morlat committed
87 88 89
}

belle_sip_resolver_context_t *belle_sip_resolver_context_new(){
Simon Morlat's avatar
Simon Morlat committed
90
	belle_sip_resolver_context_t *ctx=belle_sip_object_new(belle_sip_resolver_context_t);
91 92 93
#ifdef WIN32
	ctx->ctlevent=(belle_sip_fd_t)-1;
#endif
Simon Morlat's avatar
Simon Morlat committed
94 95 96 97 98
	return ctx;
}

static void *belle_sip_resolver_thread(void *ptr){
	belle_sip_resolver_context_t *ctx=(belle_sip_resolver_context_t *)ptr;
Simon Morlat's avatar
Simon Morlat committed
99 100 101 102 103
	struct addrinfo *res=NULL;
	struct addrinfo hints={0};
	char serv[10];
	int err;

104 105 106
	/*the thread owns a ref on the resolver context*/
	belle_sip_object_ref(ctx);
	
Simon Morlat's avatar
Simon Morlat committed
107
	belle_sip_message("Resolver thread started.");
Simon Morlat's avatar
Simon Morlat committed
108
	snprintf(serv,sizeof(serv),"%i",ctx->port);
109
	hints.ai_family=ctx->family;
Simon Morlat's avatar
Simon Morlat committed
110 111 112 113 114
	hints.ai_flags=AI_NUMERICSERV;
	err=getaddrinfo(ctx->name,serv,&hints,&res);
	if (err!=0){
		belle_sip_error("DNS resolution of %s failed: %s",ctx->name,gai_strerror(err));
	}else{
Simon Morlat's avatar
Simon Morlat committed
115 116 117
		char host[64];
		belle_sip_addrinfo_to_ip(res,host,sizeof(host),NULL);
		belle_sip_message("%s has address %s.",ctx->name,host);
Simon Morlat's avatar
Simon Morlat committed
118 119
		ctx->ai=res;
	}
120
	ctx->done=TRUE;
Simon Morlat's avatar
Simon Morlat committed
121
#ifndef WIN32
Simon Morlat's avatar
Simon Morlat committed
122 123 124
	if (write(ctx->ctlpipe[1],"q",1)==-1){
		belle_sip_error("belle_sip_resolver_thread(): Fail to write on pipe.");
	}
125 126
#else
	SetEvent(ctx->ctlevent);
Simon Morlat's avatar
Simon Morlat committed
127
#endif
128
	belle_sip_object_unref(ctx);
Simon Morlat's avatar
Simon Morlat committed
129 130 131
	return NULL;
}

132 133 134 135 136 137 138 139 140
static void belle_sip_resolver_context_start(belle_sip_resolver_context_t *ctx){
	belle_sip_fd_t fd=(belle_sip_fd_t)-1;
	belle_sip_thread_create(&ctx->thread,NULL,belle_sip_resolver_thread,ctx);
#ifndef WIN32
	if (pipe(ctx->ctlpipe)==-1){
		belle_sip_fatal("pipe() failed: %s",strerror(errno));
	}
	fd=ctx->ctlpipe[0];
#else
141 142 143 144 145
	/*we don't use the thread handle itself, because it is not a manual-reset event.
	The mainloop implementation can only work with manual-reset events*/
	ctx->ctlevent=CreateEvent(NULL,TRUE,FALSE,NULL);
	/*use CreateEventEx on wp8*/
	fd=(HANDLE)ctx->ctlevent;
146 147 148 149 150
#endif
	belle_sip_fd_source_init(&ctx->source,(belle_sip_source_func_t)resolver_callback,ctx,fd,BELLE_SIP_EVENT_READ,-1);
}

unsigned long belle_sip_resolve(const char *name, int port, int family, belle_sip_resolver_callback_t cb , void *data, belle_sip_main_loop_t *ml){
151 152 153 154 155 156 157 158
	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_resolver_context_new();
		ctx->cb_data=data;
		ctx->cb=cb;
		ctx->name=belle_sip_strdup(name);
		ctx->port=port;
159 160 161 162 163
		if (family==0) family=AF_UNSPEC;
		ctx->family=family;
		
		belle_sip_resolver_context_start(ctx);
		/*the resolver context must never be removed manually from the main loop*/
164
		belle_sip_main_loop_add_source(ml,(belle_sip_source_t*)ctx);
165
		belle_sip_object_unref(ctx);/*the main loop and the thread have a ref on it*/
166 167 168 169 170
		return ctx->source.id;
	}else{
		cb(data,name,res);
		return 0;
	}
Simon Morlat's avatar
Simon Morlat committed
171 172
}

173 174 175 176 177 178 179 180 181 182
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;
		}
	}
}

183
void belle_sip_get_src_addr_for(const struct sockaddr *dest, socklen_t destlen, struct sockaddr *src, socklen_t *srclen){
184
	int af_type=dest->sa_family;
185 186
	int sock=socket(af_type,SOCK_DGRAM,IPPROTO_UDP);
	
187
	if (sock==(belle_sip_socket_t)-1){
188 189
		belle_sip_fatal("Could not create socket: %s",belle_sip_get_socket_error_string());
		goto fail;
190 191
	}
	if (connect(sock,dest,destlen)==-1){
192 193
		belle_sip_error("belle_sip_get_src_addr_for: connect() failed: %s",belle_sip_get_socket_error_string());
		goto fail;
194 195
	}
	if (getsockname(sock,src,srclen)==-1){
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
		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);
211
	}
212
	if (sock==(belle_sip_socket_t)-1) close_socket(sock);
213 214
}