1 /*
2  * This file is part of the Sofia-SIP package
3  *
4  * Copyright (C) 2006 Nokia Corporation.
5  *
6  * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24 
25 /**@internal
26  * @file stun_dns.c
27  * @brief Functins to discover STUN server address using DNS-SRV.
28  *
29  * Refs:
30  *   - RFC3489/3489bis
31  *   - RFC2782
32  *
33  * @author Kai Vehmanen <kai.vehmanen@nokia.com>
34  */
35 
36 #include "config.h"
37 
38 #define STUN_SRV_SERVICE_TCP "_stun._tcp"
39 #define STUN_SRV_SERVICE_UDP "_stun._udp"
40 
41 #include <sofia-sip/stun.h>
42 #include <sofia-sip/su.h>
43 #include <sofia-sip/su_alloc.h>
44 #include <sofia-sip/su_wait.h>
45 #define SRES_CONTEXT_T stun_dns_lookup_t
46 #include <sofia-sip/sresolv.h>
47 
48 #if HAVE_FUNC
49 #elif HAVE_FUNCTION
50 #define __func__ __FUNCTION__
51 #else
52 static char const __func__[] = "stun_dns";
53 #endif
54 
55 #include "stun_internal.h"
56 
57 #include <string.h>
58 
59 struct stun_dns_lookup_s {
60   su_home_t          stun_home[1];
61   su_root_t         *stun_root;
62   stun_magic_t      *stun_magic;
63   sres_resolver_t   *stun_sres;
64   su_socket_t        stun_socket;
65   stun_dns_lookup_f  stun_cb;
66   char              *stun_tcp_target;
67   char              *stun_udp_target;
68   uint16_t           stun_tcp_port;
69   uint16_t           stun_udp_port;
70   unsigned           stun_state:2;       /**< bit0:udp, bit1:tcp */
71 };
72 
73 enum stun_dns_state {
74   stun_dns_udp = 1,
75   stun_dns_tls = 2,
76   stun_dns_done = stun_dns_udp | stun_dns_tls
77 };
78 
79 /**
80  * Internal callback used for gathering DNS replies.
81  */
priv_sres_cb(stun_dns_lookup_t * self,sres_query_t * q,sres_record_t ** answer)82 static void priv_sres_cb(stun_dns_lookup_t *self,
83 			 sres_query_t *q,
84 			 sres_record_t **answer)
85 {
86   int i;
87 
88   sres_sort_answers(self->stun_sres, answer);
89 
90   /* note: picks the first ones (sort puts records with most
91    *       weight at start */
92 
93   for (i = 0; answer && answer[i] != NULL; i++) {
94     sres_srv_record_t *rr = (sres_srv_record_t *) answer[i]->sr_srv;
95     if (rr && rr->srv_record && rr->srv_record->r_type == sres_type_srv) {
96       const char *tcp_name = STUN_SRV_SERVICE_TCP;
97       const char *udp_name = STUN_SRV_SERVICE_UDP;
98       if ((self->stun_state & stun_dns_tls) == 0 &&
99 	  strncmp(rr->srv_record->r_name, tcp_name, strlen(tcp_name)) == 0) {
100 	self->stun_tcp_target = su_strdup(self->stun_home, rr->srv_target);
101 	self->stun_tcp_port = rr->srv_port;
102 	self->stun_state |= stun_dns_tls;
103 	SU_DEBUG_5(("%s: stun (tcp) for domain %s is at %s:%u.\n",
104 		    __func__, rr->srv_record->r_name, self->stun_tcp_target, self->stun_tcp_port));
105       }
106       else if ((self->stun_state & stun_dns_udp) == 0 &&
107 	       strncmp(rr->srv_record->r_name, udp_name, strlen(udp_name)) == 0) {
108 	self->stun_udp_target = su_strdup(self->stun_home, rr->srv_target);
109 	self->stun_udp_port = rr->srv_port;
110 	self->stun_state |= stun_dns_udp;
111 	SU_DEBUG_5(("%s: stun (udp) for domain %s is at %s:%u.\n",
112 		    __func__, rr->srv_record->r_name, self->stun_udp_target, self->stun_udp_port));
113       }
114     }
115   }
116 
117   if (self->stun_state == stun_dns_done) {
118     self->stun_cb(self, self->stun_magic);
119 
120     sres_resolver_timer(self->stun_sres, -1);
121   }
122 
123   sres_free_answers(self->stun_sres, answer);
124 }
125 
126 /**
127  * Performs a DNS-SRV check for STUN 'stun' (tcp) and
128  * 'stun' (udp) services for 'domain'.
129  *
130  * The result will be delivered asynchronously in the
131  * 'func' callback. 'root' will be used as the event loop.
132  */
stun_dns_lookup(stun_magic_t * magic,su_root_t * root,stun_dns_lookup_f func,const char * domain)133 stun_dns_lookup_t *stun_dns_lookup(stun_magic_t *magic,
134 				   su_root_t *root,
135 				   stun_dns_lookup_f func,
136 				   const char *domain)
137 {
138   stun_dns_lookup_t *self;
139   sres_query_t *query;
140 
141   if (!domain ||
142       strlen(domain) + strlen(STUN_SRV_SERVICE_UDP ".") + 1>= SRES_MAXDNAME)
143     return NULL;
144 
145   self = su_home_new(sizeof(stun_dns_lookup_t));
146 
147   /* see nta.c:outgoing_answer_srv() */
148   self->stun_magic = magic;
149   self->stun_cb = func;
150   self->stun_root = root;
151   self->stun_sres = sres_resolver_create(root, NULL, TAG_END());
152   if (self->stun_sres) {
153     char srvname[SRES_MAXDNAME + 1];
154 
155     snprintf(srvname, sizeof srvname, "%s.%s", STUN_SRV_SERVICE_UDP, domain);
156 
157     query = sres_query(self->stun_sres, priv_sres_cb, self,
158 		       sres_type_srv,
159 		       srvname);
160 
161     snprintf(srvname, sizeof srvname, "%s.%s", STUN_SRV_SERVICE_TCP, domain);
162 
163     query = sres_query(self->stun_sres, priv_sres_cb, self,
164 		       sres_type_srv,
165 		       srvname);
166     if (query) {
167       /* TODO Just so compiler doesn't give error */
168     }
169 }
170   else {
171     su_free(NULL, self), self = NULL;
172   }
173 
174   return self;
175 }
176 
177 /**
178  * Destroys the 'self' object created by stun_dns_lookup_destroy().
179  */
stun_dns_lookup_destroy(stun_dns_lookup_t * self)180 void stun_dns_lookup_destroy(stun_dns_lookup_t *self)
181 {
182   if (self->stun_sres)
183     sres_resolver_destroy(self->stun_sres);
184 
185   su_home_unref(self->stun_home);
186 }
187 
188 /**
189  * Fetches the results of a completed STUN DNS-SRV lookup
190  * for "_stun._udp" service (RFC3489/3489bis).
191  *
192  * @param self context pointer
193  * @param target location where to stored the 'target'
194  *        SRV field for stun service
195  * @param port location where to store port number
196  *
197  * @return 0 on success, non-zero otherwise
198  */
stun_dns_lookup_udp_addr(stun_dns_lookup_t * self,const char ** target,uint16_t * port)199 int stun_dns_lookup_udp_addr(stun_dns_lookup_t *self, const char **target, uint16_t *port)
200 {
201   int result = -1;
202   if (self->stun_state == stun_dns_done) {
203     if (target) *target = self->stun_udp_target;
204     if (port) *port = self->stun_udp_port;
205     result = 0;
206   }
207 
208   return result;
209 }
210 
211 /**
212  * Fetches the results of a completed STUN DNS-SRV lookup
213  * for "_stun._tcp" service (RFC3489).
214  *
215  * @param self context pointer
216  * @param target location where to stored the 'target'
217  *        SRV field for stun service
218  * @param port location where to store port number
219  *
220  * @return 0 on success, non-zero otherwise
221  */
stun_dns_lookup_tcp_addr(stun_dns_lookup_t * self,const char ** target,uint16_t * port)222 int stun_dns_lookup_tcp_addr(stun_dns_lookup_t *self, const char **target, uint16_t *port)
223 {
224   int result = -1;
225   if (self->stun_state == stun_dns_done) {
226     if (target) *target = self->stun_tcp_target;
227     if (port) *port = self->stun_tcp_port;
228     result = 0;
229   }
230 
231   return result;
232 }
233 
234 /**
235  * Fetches the results of a completed STUN DNS-SRV lookup
236  * for "_stun._udp" service (3489bis, "Short-Term Password").
237  *
238  * @param self context pointer
239  * @param target location where to stored the 'target'
240  *        SRV field for stun service
241  * @param port location where to store port number
242  *
243  * @return 0 on success, non-zero otherwise
244  */
stun_dns_lookup_stp_addr(stun_dns_lookup_t * self,const char ** target,uint16_t * port)245 int stun_dns_lookup_stp_addr(stun_dns_lookup_t *self, const char **target, uint16_t *port)
246 {
247   /* XXX: not implemented */
248   return -1;
249 }
250 
251 
252