Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjnath / src / pjnath / stun_transaction.c
1 /* $Id$ */
2 /* 
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
19  */
20 #include <pjnath/stun_transaction.h>
21 #include <pjnath/errno.h>
22 #include <pj/assert.h>
23 #include <pj/log.h>
24 #include <pj/pool.h>
25 #include <pj/string.h>
26 #include <pj/timer.h>
27
28
29 #define TIMER_ACTIVE            1
30
31
32 struct pj_stun_client_tsx
33 {
34     char                 obj_name[PJ_MAX_OBJ_NAME];
35     pj_stun_tsx_cb       cb;
36     void                *user_data;
37
38     pj_bool_t            complete;
39
40     pj_bool_t            require_retransmit;
41     unsigned             rto_msec;
42     pj_timer_entry       retransmit_timer;
43     unsigned             transmit_count;
44     pj_time_val          retransmit_time;
45     pj_timer_heap_t     *timer_heap;
46
47     pj_timer_entry       destroy_timer;
48
49     void                *last_pkt;
50     unsigned             last_pkt_size;
51 };
52
53
54 static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, 
55                                       pj_timer_entry *timer);
56 static void destroy_timer_callback(pj_timer_heap_t *timer_heap, 
57                                    pj_timer_entry *timer);
58
59 #define stun_perror(tsx,msg,rc) pjnath_perror(tsx->obj_name, msg, rc)
60
61 /*
62  * Create a STUN client transaction.
63  */
64 PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg,
65                                               pj_pool_t *pool,
66                                               const pj_stun_tsx_cb *cb,
67                                               pj_stun_client_tsx **p_tsx)
68 {
69     pj_stun_client_tsx *tsx;
70
71     PJ_ASSERT_RETURN(cfg && cb && p_tsx, PJ_EINVAL);
72     PJ_ASSERT_RETURN(cb->on_send_msg, PJ_EINVAL);
73
74     tsx = PJ_POOL_ZALLOC_T(pool, pj_stun_client_tsx);
75     tsx->rto_msec = cfg->rto_msec;
76     tsx->timer_heap = cfg->timer_heap;
77     pj_memcpy(&tsx->cb, cb, sizeof(*cb));
78
79     tsx->retransmit_timer.cb = &retransmit_timer_callback;
80     tsx->retransmit_timer.user_data = tsx;
81
82     tsx->destroy_timer.cb = &destroy_timer_callback;
83     tsx->destroy_timer.user_data = tsx;
84
85     pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "stuntsx%p", tsx);
86
87     *p_tsx = tsx;
88
89     PJ_LOG(5,(tsx->obj_name, "STUN client transaction created"));
90     return PJ_SUCCESS;
91 }
92
93
94 PJ_DEF(pj_status_t) pj_stun_client_tsx_schedule_destroy(
95                                     pj_stun_client_tsx *tsx,
96                                     const pj_time_val *delay)
97 {
98     pj_status_t status;
99
100     PJ_ASSERT_RETURN(tsx && delay, PJ_EINVAL);
101     PJ_ASSERT_RETURN(tsx->cb.on_destroy, PJ_EINVAL);
102
103     /* Cancel previously registered timer */
104     if (tsx->destroy_timer.id != 0) {
105         pj_timer_heap_cancel(tsx->timer_heap, &tsx->destroy_timer);
106         tsx->destroy_timer.id = 0;
107     }
108
109     /* Stop retransmission, just in case */
110     if (tsx->retransmit_timer.id != 0) {
111         pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
112         tsx->retransmit_timer.id = 0;
113     }
114
115     status = pj_timer_heap_schedule(tsx->timer_heap,
116                                     &tsx->destroy_timer, delay);
117     if (status != PJ_SUCCESS)
118         return status;
119
120     tsx->destroy_timer.id = TIMER_ACTIVE;
121     tsx->cb.on_complete = NULL;
122
123     return PJ_SUCCESS;
124 }
125
126
127 /*
128  * Destroy transaction immediately.
129  */
130 PJ_DEF(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx)
131 {
132     PJ_ASSERT_RETURN(tsx, PJ_EINVAL);
133
134     if (tsx->retransmit_timer.id != 0) {
135         pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
136         tsx->retransmit_timer.id = 0;
137     }
138     if (tsx->destroy_timer.id != 0) {
139         pj_timer_heap_cancel(tsx->timer_heap, &tsx->destroy_timer);
140         tsx->destroy_timer.id = 0;
141     }
142
143     PJ_LOG(5,(tsx->obj_name, "STUN client transaction destroyed"));
144     return PJ_SUCCESS;
145 }
146
147
148 /*
149  * Check if transaction has completed.
150  */
151 PJ_DEF(pj_bool_t) pj_stun_client_tsx_is_complete(pj_stun_client_tsx *tsx)
152 {
153     PJ_ASSERT_RETURN(tsx, PJ_FALSE);
154     return tsx->complete;
155 }
156
157
158 /*
159  * Set user data.
160  */
161 PJ_DEF(pj_status_t) pj_stun_client_tsx_set_data(pj_stun_client_tsx *tsx,
162                                                 void *data)
163 {
164     PJ_ASSERT_RETURN(tsx, PJ_EINVAL);
165     tsx->user_data = data;
166     return PJ_SUCCESS;
167 }
168
169
170 /*
171  * Get the user data
172  */
173 PJ_DEF(void*) pj_stun_client_tsx_get_data(pj_stun_client_tsx *tsx)
174 {
175     PJ_ASSERT_RETURN(tsx, NULL);
176     return tsx->user_data;
177 }
178
179
180 /*
181  * Transmit message.
182  */
183 static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx)
184 {
185     pj_status_t status;
186
187     PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0 ||
188                      !tsx->require_retransmit, PJ_EBUSY);
189
190     if (tsx->require_retransmit) {
191         /* Calculate retransmit/timeout delay */
192         if (tsx->transmit_count == 0) {
193             tsx->retransmit_time.sec = 0;
194             tsx->retransmit_time.msec = tsx->rto_msec;
195
196         } else if (tsx->transmit_count < PJ_STUN_MAX_TRANSMIT_COUNT-1) {
197             unsigned msec;
198
199             msec = PJ_TIME_VAL_MSEC(tsx->retransmit_time);
200             msec <<= 1;
201             tsx->retransmit_time.sec = msec / 1000;
202             tsx->retransmit_time.msec = msec % 1000;
203
204         } else {
205             tsx->retransmit_time.sec = PJ_STUN_TIMEOUT_VALUE / 1000;
206             tsx->retransmit_time.msec = PJ_STUN_TIMEOUT_VALUE % 1000;
207         }
208
209         /* Schedule timer first because when send_msg() failed we can
210          * cancel it (as opposed to when schedule_timer() failed we cannot
211          * cancel transmission).
212          */;
213         status = pj_timer_heap_schedule(tsx->timer_heap, 
214                                         &tsx->retransmit_timer,
215                                         &tsx->retransmit_time);
216         if (status != PJ_SUCCESS) {
217             tsx->retransmit_timer.id = 0;
218             return status;
219         }
220         tsx->retransmit_timer.id = TIMER_ACTIVE;
221     }
222
223
224     tsx->transmit_count++;
225
226     PJ_LOG(5,(tsx->obj_name, "STUN sending message (transmit count=%d)",
227               tsx->transmit_count));
228     pj_log_push_indent();
229
230     /* Send message */
231     status = tsx->cb.on_send_msg(tsx, tsx->last_pkt, tsx->last_pkt_size);
232
233     if (status == PJNATH_ESTUNDESTROYED) {
234         /* We've been destroyed, don't access the object. */
235     } else if (status != PJ_SUCCESS) {
236         if (tsx->retransmit_timer.id != 0) {
237             pj_timer_heap_cancel(tsx->timer_heap, 
238                                  &tsx->retransmit_timer);
239             tsx->retransmit_timer.id = 0;
240         }
241         stun_perror(tsx, "STUN error sending message", status);
242     }
243
244     pj_log_pop_indent();
245     return status;
246 }
247
248
249 /*
250  * Send outgoing message and start STUN transaction.
251  */
252 PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx,
253                                                 pj_bool_t retransmit,
254                                                 void *pkt,
255                                                 unsigned pkt_len)
256 {
257     pj_status_t status;
258
259     PJ_ASSERT_RETURN(tsx && pkt && pkt_len, PJ_EINVAL);
260     PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0, PJ_EBUSY);
261
262     /* Encode message */
263     tsx->last_pkt = pkt;
264     tsx->last_pkt_size = pkt_len;
265
266     /* Update STUN retransmit flag */
267     tsx->require_retransmit = retransmit;
268
269     /* For TCP, schedule timeout timer after PJ_STUN_TIMEOUT_VALUE.
270      * Since we don't have timeout timer, simulate this by using
271      * retransmit timer.
272      */
273     if (!retransmit) {
274         unsigned timeout;
275
276         pj_assert(tsx->retransmit_timer.id == 0);
277         tsx->transmit_count = PJ_STUN_MAX_TRANSMIT_COUNT;
278
279         timeout = tsx->rto_msec * 16;
280         tsx->retransmit_time.sec = timeout / 1000;
281         tsx->retransmit_time.msec = timeout % 1000;
282
283         /* Schedule timer first because when send_msg() failed we can
284          * cancel it (as opposed to when schedule_timer() failed we cannot
285          * cancel transmission).
286          */;
287         status = pj_timer_heap_schedule(tsx->timer_heap, 
288                                         &tsx->retransmit_timer,
289                                         &tsx->retransmit_time);
290         if (status != PJ_SUCCESS) {
291             tsx->retransmit_timer.id = 0;
292             return status;
293         }
294         tsx->retransmit_timer.id = TIMER_ACTIVE;
295     }
296
297     /* Send the message */
298     status = tsx_transmit_msg(tsx);
299     if (status != PJ_SUCCESS) {
300         if (tsx->retransmit_timer.id != 0) {
301             pj_timer_heap_cancel(tsx->timer_heap, 
302                                  &tsx->retransmit_timer);
303             tsx->retransmit_timer.id = 0;
304         }
305         return status;
306     }
307
308     return PJ_SUCCESS;
309 }
310
311
312 /* Retransmit timer callback */
313 static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, 
314                                       pj_timer_entry *timer)
315 {
316     pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data;
317     pj_status_t status;
318
319     PJ_UNUSED_ARG(timer_heap);
320
321     if (tsx->transmit_count >= PJ_STUN_MAX_TRANSMIT_COUNT) {
322         /* Retransmission count exceeded. Transaction has failed */
323         tsx->retransmit_timer.id = 0;
324         PJ_LOG(4,(tsx->obj_name, "STUN timeout waiting for response"));
325         pj_log_push_indent();
326         if (!tsx->complete) {
327             tsx->complete = PJ_TRUE;
328             if (tsx->cb.on_complete) {
329                 tsx->cb.on_complete(tsx, PJNATH_ESTUNTIMEDOUT, NULL, NULL, 0);
330             }
331         }
332         /* We might have been destroyed, don't try to access the object */
333         pj_log_pop_indent();
334         return;
335     }
336
337     tsx->retransmit_timer.id = 0;
338     status = tsx_transmit_msg(tsx);
339     if (status == PJNATH_ESTUNDESTROYED) {
340         /* We've been destroyed, don't try to access the object */
341     } else if (status != PJ_SUCCESS) {
342         tsx->retransmit_timer.id = 0;
343         if (!tsx->complete) {
344             tsx->complete = PJ_TRUE;
345             if (tsx->cb.on_complete) {
346                 tsx->cb.on_complete(tsx, status, NULL, NULL, 0);
347             }
348         }
349         /* We might have been destroyed, don't try to access the object */
350     }
351 }
352
353 /*
354  * Request to retransmit the request.
355  */
356 PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx)
357 {
358     if (tsx->destroy_timer.id != 0) {
359         return PJ_SUCCESS;
360     }
361
362     if (tsx->retransmit_timer.id != 0) {
363         pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
364         tsx->retransmit_timer.id = 0;
365     }
366
367     return tsx_transmit_msg(tsx);
368 }
369
370 /* Timer callback to destroy transaction */
371 static void destroy_timer_callback(pj_timer_heap_t *timer_heap, 
372                                    pj_timer_entry *timer)
373 {
374     pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data;
375
376     PJ_UNUSED_ARG(timer_heap);
377
378     tsx->destroy_timer.id = PJ_FALSE;
379     tsx->cb.on_destroy(tsx);
380     /* Don't access transaction after this */
381 }
382
383
384 /*
385  * Notify the STUN transaction about the arrival of STUN response.
386  */
387 PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx,
388                                                  const pj_stun_msg *msg,
389                                                  const pj_sockaddr_t *src_addr,
390                                                  unsigned src_addr_len)
391 {
392     pj_stun_errcode_attr *err_attr;
393     pj_status_t status;
394
395     /* Must be STUN response message */
396     if (!PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) && 
397         !PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
398     {
399         PJ_LOG(4,(tsx->obj_name, 
400                   "STUN rx_msg() error: not response message"));
401         return PJNATH_EINSTUNMSGTYPE;
402     }
403
404
405     /* We have a response with matching transaction ID. 
406      * We can cancel retransmit timer now.
407      */
408     if (tsx->retransmit_timer.id) {
409         pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
410         tsx->retransmit_timer.id = 0;
411     }
412
413     /* Find STUN error code attribute */
414     err_attr = (pj_stun_errcode_attr*) 
415                 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ERROR_CODE, 0);
416
417     if (err_attr && err_attr->err_code <= 200) {
418         /* draft-ietf-behave-rfc3489bis-05.txt Section 8.3.2:
419          * Any response between 100 and 299 MUST result in the cessation
420          * of request retransmissions, but otherwise is discarded.
421          */
422         PJ_LOG(4,(tsx->obj_name, 
423                   "STUN rx_msg() error: received provisional %d code (%.*s)",
424                   err_attr->err_code,
425                   (int)err_attr->reason.slen,
426                   err_attr->reason.ptr));
427         return PJ_SUCCESS;
428     }
429
430     if (err_attr == NULL) {
431         status = PJ_SUCCESS;
432     } else {
433         status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code);
434     }
435
436     /* Call callback */
437     if (!tsx->complete) {
438         tsx->complete = PJ_TRUE;
439         if (tsx->cb.on_complete) {
440             tsx->cb.on_complete(tsx, status, msg, src_addr, src_addr_len);
441         }
442         /* We might have been destroyed, don't try to access the object */
443     }
444
445     return PJ_SUCCESS;
446
447 }
448