blob: a3468ef4a419b7aea3ff024372bde220965d4ed6 [file] [log] [blame]
Benny Prijonofff245c2007-04-02 11:44:47 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijonofff245c2007-04-02 11:44:47 +00005 *
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#define THIS_FILE "stateful_proxy.c"
21
22/* Common proxy functions */
23#define STATEFUL 1
24#include "proxy.h"
25
26
27/*
28 * mod_stateful_proxy is the module to receive SIP request and
29 * response message that is outside any transaction context.
30 */
31static pj_bool_t proxy_on_rx_request(pjsip_rx_data *rdata );
32static pj_bool_t proxy_on_rx_response(pjsip_rx_data *rdata );
33
34static pjsip_module mod_stateful_proxy =
35{
36 NULL, NULL, /* prev, next. */
37 { "mod-stateful-proxy", 18 }, /* Name. */
38 -1, /* Id */
39 PJSIP_MOD_PRIORITY_UA_PROXY_LAYER, /* Priority */
40 NULL, /* load() */
41 NULL, /* start() */
42 NULL, /* stop() */
43 NULL, /* unload() */
44 &proxy_on_rx_request, /* on_rx_request() */
45 &proxy_on_rx_response, /* on_rx_response() */
46 NULL, /* on_tx_request. */
47 NULL, /* on_tx_response() */
48 NULL, /* on_tsx_state() */
49};
50
51
52/*
53 * mod_tu (tu=Transaction User) is the module to receive notification
54 * from transaction when the transaction state has changed.
55 */
56static void tu_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event);
57
58static pjsip_module mod_tu =
59{
60 NULL, NULL, /* prev, next. */
61 { "mod-transaction-user", 20 }, /* Name. */
62 -1, /* Id */
63 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
64 NULL, /* load() */
65 NULL, /* start() */
66 NULL, /* stop() */
67 NULL, /* unload() */
68 NULL, /* on_rx_request() */
69 NULL, /* on_rx_response() */
70 NULL, /* on_tx_request. */
71 NULL, /* on_tx_response() */
72 &tu_on_tsx_state, /* on_tsx_state() */
73};
74
75
76/* This is the data that is attached to the UAC transaction */
77struct uac_data
78{
79 pjsip_transaction *uas_tsx;
80 pj_timer_entry timer;
81};
82
83
84/* This is the data that is attached to the UAS transaction */
85struct uas_data
86{
87 pjsip_transaction *uac_tsx;
88};
89
90
91
92static pj_status_t init_stateful_proxy(void)
93{
94 pj_status_t status;
95
96 status = pjsip_endpt_register_module( global.endpt, &mod_stateful_proxy);
97 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
98
99 status = pjsip_endpt_register_module( global.endpt, &mod_tu);
100 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
101
102 return PJ_SUCCESS;
103}
104
105
106/* Callback to be called to handle new incoming requests. */
107static pj_bool_t proxy_on_rx_request( pjsip_rx_data *rdata )
108{
109 pjsip_transaction *uas_tsx, *uac_tsx;
110 struct uac_data *uac_data;
111 struct uas_data *uas_data;
112 pjsip_tx_data *tdata;
113 pj_status_t status;
114
115 if (rdata->msg_info.msg->line.req.method.id != PJSIP_CANCEL_METHOD) {
116
117 /* Verify incoming request */
118 status = proxy_verify_request(rdata);
119 if (status != PJ_SUCCESS) {
120 app_perror("RX invalid request", status);
121 return PJ_TRUE;
122 }
123
124 /*
125 * Request looks sane, next clone the request to create transmit data.
126 */
127 status = pjsip_endpt_create_request_fwd(global.endpt, rdata, NULL,
128 NULL, 0, &tdata);
129 if (status != PJ_SUCCESS) {
130 pjsip_endpt_respond_stateless(global.endpt, rdata,
131 PJSIP_SC_INTERNAL_SERVER_ERROR,
132 NULL, NULL, NULL);
133 return PJ_TRUE;
134 }
135
136
137 /* Process routing */
138 status = proxy_process_routing(tdata);
139 if (status != PJ_SUCCESS) {
140 app_perror("Error processing route", status);
141 return PJ_TRUE;
142 }
143
144 /* Calculate target */
145 status = proxy_calculate_target(rdata, tdata);
146 if (status != PJ_SUCCESS) {
147 app_perror("Error calculating target", status);
148 return PJ_TRUE;
149 }
150
151 /* Everything is set to forward the request. */
152
153 /* If this is an ACK request, forward statelessly.
154 * This happens if the proxy records route and this ACK
155 * is sent for 2xx response. An ACK that is sent for non-2xx
156 * final response will be absorbed by transaction layer, and
157 * it will not be received by on_rx_request() callback.
158 */
159 if (tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) {
160 status = pjsip_endpt_send_request_stateless(global.endpt, tdata,
161 NULL, NULL);
162 if (status != PJ_SUCCESS) {
163 app_perror("Error forwarding request", status);
164 return PJ_TRUE;
165 }
166
167 return PJ_TRUE;
168 }
169
170 /* Create UAC transaction for forwarding the request.
171 * Set our module as the transaction user to receive further
172 * events from this transaction.
173 */
174 status = pjsip_tsx_create_uac(&mod_tu, tdata, &uac_tsx);
175 if (status != PJ_SUCCESS) {
176 pjsip_tx_data_dec_ref(tdata);
177 pjsip_endpt_respond_stateless(global.endpt, rdata,
178 PJSIP_SC_INTERNAL_SERVER_ERROR,
179 NULL, NULL, NULL);
180 return PJ_TRUE;
181 }
182
183 /* Create UAS transaction to handle incoming request */
184 status = pjsip_tsx_create_uas(&mod_tu, rdata, &uas_tsx);
185 if (status != PJ_SUCCESS) {
186 pjsip_tx_data_dec_ref(tdata);
187 pjsip_endpt_respond_stateless(global.endpt, rdata,
188 PJSIP_SC_INTERNAL_SERVER_ERROR,
189 NULL, NULL, NULL);
190 pjsip_tsx_terminate(uac_tsx, PJSIP_SC_INTERNAL_SERVER_ERROR);
191 return PJ_TRUE;
192 }
193
194 /* Feed the request to the UAS transaction to drive it's state
195 * out of NULL state.
196 */
197 pjsip_tsx_recv_msg(uas_tsx, rdata);
198
199 /* Attach a data to the UAC transaction, to be used to find the
200 * UAS transaction when we receive response in the UAC side.
201 */
202 uac_data = (struct uac_data*)
203 pj_pool_alloc(uac_tsx->pool, sizeof(struct uac_data));
204 uac_data->uas_tsx = uas_tsx;
205 uac_tsx->mod_data[mod_tu.id] = (void*)uac_data;
206
207 /* Attach data to the UAS transaction, to find the UAC transaction
208 * when cancelling INVITE request.
209 */
210 uas_data = (struct uas_data*)
211 pj_pool_alloc(uas_tsx->pool, sizeof(struct uas_data));
212 uas_data->uac_tsx = uac_tsx;
213 uas_tsx->mod_data[mod_tu.id] = (void*)uas_data;
214
215 /* Everything is setup, forward the request */
216 status = pjsip_tsx_send_msg(uac_tsx, tdata);
217 if (status != PJ_SUCCESS) {
218 pjsip_tx_data *err_res;
219
220 /* Fail to send request, for some reason */
221
222 /* Destroy transmit data */
223 pjsip_tx_data_dec_ref(tdata);
224
225 /* I think UAC transaction should have been destroyed when
226 * it fails to send request, so no need to destroy it.
227 pjsip_tsx_terminate(uac_tsx, PJSIP_SC_INTERNAL_SERVER_ERROR);
228 */
229
230 /* Send 500/Internal Server Error to UAS transaction */
231 pjsip_endpt_create_response(global.endpt, rdata,
232 500, NULL, &err_res);
233 pjsip_tsx_send_msg(uas_tsx, err_res);
234
235 return PJ_TRUE;
236 }
237
238 /* Send 100/Trying if this is an INVITE */
239 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
240 pjsip_tx_data *res100;
241
242 pjsip_endpt_create_response(global.endpt, rdata, 100, NULL,
243 &res100);
244 pjsip_tsx_send_msg(uas_tsx, res100);
245 }
246
247 } else {
248 /* This is CANCEL request */
249 pjsip_transaction *invite_uas;
250 struct uas_data *uas_data;
251 pj_str_t key;
252
253 /* Find the UAS INVITE transaction */
254 pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_UAS_ROLE,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000255 pjsip_get_invite_method(), rdata);
Benny Prijonofff245c2007-04-02 11:44:47 +0000256 invite_uas = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE);
257 if (!invite_uas) {
258 /* Invite transaction not found, respond CANCEL with 481 */
259 pjsip_endpt_respond_stateless(global.endpt, rdata, 481, NULL,
260 NULL, NULL);
261 return PJ_TRUE;
262 }
263
264 /* Respond 200 OK to CANCEL */
265 pjsip_endpt_respond(global.endpt, NULL, rdata, 200, NULL, NULL,
266 NULL, NULL);
267
268 /* Send CANCEL to cancel the UAC transaction.
269 * The UAS INVITE transaction will get final response when
270 * we receive final response from the UAC INVITE transaction.
271 */
272 uas_data = (struct uas_data*) invite_uas->mod_data[mod_tu.id];
Benny Prijonoe9e29562008-02-26 11:14:03 +0000273 if (uas_data->uac_tsx && uas_data->uac_tsx->status_code < 200) {
Benny Prijonofff245c2007-04-02 11:44:47 +0000274 pjsip_tx_data *cancel;
275
276 pj_mutex_lock(uas_data->uac_tsx->mutex);
277
278 pjsip_endpt_create_cancel(global.endpt, uas_data->uac_tsx->last_tx,
279 &cancel);
280 pjsip_endpt_send_request(global.endpt, cancel, -1, NULL, NULL);
281
282 pj_mutex_unlock(uas_data->uac_tsx->mutex);
283 }
284
285 /* Unlock UAS tsx because it is locked in find_tsx() */
286 pj_mutex_unlock(invite_uas->mutex);
287 }
288
289 return PJ_TRUE;
290}
291
292
293/* Callback to be called to handle incoming response outside
294 * any transactions. This happens for example when 2xx/OK
295 * for INVITE is received and transaction will be destroyed
296 * immediately, so we need to forward the subsequent 2xx/OK
297 * retransmission statelessly.
298 */
299static pj_bool_t proxy_on_rx_response( pjsip_rx_data *rdata )
300{
301 pjsip_tx_data *tdata;
302 pjsip_response_addr res_addr;
303 pjsip_via_hdr *hvia;
304 pj_status_t status;
305
306 /* Create response to be forwarded upstream (Via will be stripped here) */
307 status = pjsip_endpt_create_response_fwd(global.endpt, rdata, 0, &tdata);
308 if (status != PJ_SUCCESS) {
309 app_perror("Error creating response", status);
310 return PJ_TRUE;
311 }
312
313 /* Get topmost Via header */
314 hvia = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
315 if (hvia == NULL) {
316 /* Invalid response! Just drop it */
317 pjsip_tx_data_dec_ref(tdata);
318 return PJ_TRUE;
319 }
320
321 /* Calculate the address to forward the response */
322 pj_bzero(&res_addr, sizeof(res_addr));
323 res_addr.dst_host.type = PJSIP_TRANSPORT_UDP;
324 res_addr.dst_host.flag =
325 pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP);
326
327 /* Destination address is Via's received param */
328 res_addr.dst_host.addr.host = hvia->recvd_param;
329 if (res_addr.dst_host.addr.host.slen == 0) {
330 /* Someone has messed up our Via header! */
331 res_addr.dst_host.addr.host = hvia->sent_by.host;
332 }
333
334 /* Destination port is the rport */
335 if (hvia->rport_param != 0 && hvia->rport_param != -1)
336 res_addr.dst_host.addr.port = hvia->rport_param;
337
338 if (res_addr.dst_host.addr.port == 0) {
339 /* Ugh, original sender didn't put rport!
340 * At best, can only send the response to the port in Via.
341 */
342 res_addr.dst_host.addr.port = hvia->sent_by.port;
343 }
344
345 /* Forward response */
346 status = pjsip_endpt_send_response(global.endpt, &res_addr, tdata,
347 NULL, NULL);
348 if (status != PJ_SUCCESS) {
349 app_perror("Error forwarding response", status);
350 return PJ_TRUE;
351 }
352
353 return PJ_TRUE;
354}
355
356
357/* Callback to be called to handle transaction state changed. */
358static void tu_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
359{
360 struct uac_data *uac_data;
361 pj_status_t status;
362
363 if (tsx->role == PJSIP_ROLE_UAS) {
364 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
365 struct uas_data *uas_data;
366
367 uas_data = (struct uas_data*) tsx->mod_data[mod_tu.id];
368 if (uas_data->uac_tsx) {
369 uac_data = (struct uac_data*)
370 uas_data->uac_tsx->mod_data[mod_tu.id];
371 uac_data->uas_tsx = NULL;
372 }
373
374 }
375 return;
376 }
377
378 /* Get the data that we attached to the UAC transaction previously */
379 uac_data = (struct uac_data*) tsx->mod_data[mod_tu.id];
380
381
382 /* Handle incoming response */
383 if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
384
385 pjsip_rx_data *rdata;
386 pjsip_response_addr res_addr;
387 pjsip_via_hdr *hvia;
388 pjsip_tx_data *tdata;
389
390 rdata = event->body.tsx_state.src.rdata;
391
392 /* Do not forward 100 response for INVITE (we already responded
393 * INVITE with 100)
394 */
395 if (tsx->method.id == PJSIP_INVITE_METHOD &&
396 rdata->msg_info.msg->line.status.code == 100)
397 {
398 return;
399 }
400
401 /* Create response to be forwarded upstream
402 * (Via will be stripped here)
403 */
404 status = pjsip_endpt_create_response_fwd(global.endpt, rdata, 0,
405 &tdata);
406 if (status != PJ_SUCCESS) {
407 app_perror("Error creating response", status);
408 return;
409 }
410
411 /* Get topmost Via header of the new response */
412 hvia = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA,
413 NULL);
414 if (hvia == NULL) {
415 /* Invalid response! Just drop it */
416 pjsip_tx_data_dec_ref(tdata);
417 return;
418 }
419
420 /* Calculate the address to forward the response */
421 pj_bzero(&res_addr, sizeof(res_addr));
422 res_addr.dst_host.type = PJSIP_TRANSPORT_UDP;
423 res_addr.dst_host.flag =
424 pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP);
425
426 /* Destination address is Via's received param */
427 res_addr.dst_host.addr.host = hvia->recvd_param;
428 if (res_addr.dst_host.addr.host.slen == 0) {
429 /* Someone has messed up our Via header! */
430 res_addr.dst_host.addr.host = hvia->sent_by.host;
431 }
432
433 /* Destination port is the rport */
434 if (hvia->rport_param != 0 && hvia->rport_param != -1)
435 res_addr.dst_host.addr.port = hvia->rport_param;
436
437 if (res_addr.dst_host.addr.port == 0) {
438 /* Ugh, original sender didn't put rport!
439 * At best, can only send the response to the port in Via.
440 */
441 res_addr.dst_host.addr.port = hvia->sent_by.port;
442 }
443
444 /* Forward response with the UAS transaction */
445 pjsip_tsx_send_msg(uac_data->uas_tsx, tdata);
446
447 }
448
449 /* If UAC transaction is terminated, terminate the UAS as well.
450 * This could happen because of:
451 * - timeout on the UAC side
452 * - receipt of 2xx response to INVITE
453 */
Benny Prijonoe2bb6072008-07-23 09:59:11 +0000454 if (tsx->state == PJSIP_TSX_STATE_TERMINATED && uac_data &&
455 uac_data->uas_tsx)
456 {
Benny Prijonofff245c2007-04-02 11:44:47 +0000457
458 pjsip_transaction *uas_tsx;
459 struct uas_data *uas_data;
460
461 uas_tsx = uac_data->uas_tsx;
462 uas_data = (struct uas_data*) uas_tsx->mod_data[mod_tu.id];
463 uas_data->uac_tsx = NULL;
464
465 if (event->body.tsx_state.type == PJSIP_EVENT_TIMER) {
466
467 /* Send 408/Timeout if this is an INVITE transaction, since
468 * we must have sent provisional response before. For non
469 * INVITE transaction, just destroy it.
470 */
471 if (tsx->method.id == PJSIP_INVITE_METHOD) {
472
473 pjsip_tx_data *tdata = uas_tsx->last_tx;
474
475 tdata->msg->line.status.code = PJSIP_SC_REQUEST_TIMEOUT;
476 tdata->msg->line.status.reason = pj_str("Request timed out");
477 tdata->msg->body = NULL;
478
479 pjsip_tx_data_add_ref(tdata);
480 pjsip_tx_data_invalidate_msg(tdata);
481
482 pjsip_tsx_send_msg(uas_tsx, tdata);
483
484 } else {
485 /* For non-INVITE, just destroy the UAS transaction */
486 pjsip_tsx_terminate(uas_tsx, PJSIP_SC_REQUEST_TIMEOUT);
487 }
488
489 } else if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
490
491 if (uas_tsx->state < PJSIP_TSX_STATE_TERMINATED) {
492 pjsip_msg *msg;
493 int code;
494
495 msg = event->body.tsx_state.src.rdata->msg_info.msg;
496 code = msg->line.status.code;
497
498 uac_data->uas_tsx = NULL;
499 pjsip_tsx_terminate(uas_tsx, code);
500 }
501 }
502 }
503}
504
505
506/*
507 * main()
508 */
509int main(int argc, char *argv[])
510{
511 pj_status_t status;
512
513 global.port = 5060;
514 global.record_route = 0;
515
516 pj_log_set_level(4);
517
518 status = init_options(argc, argv);
519 if (status != PJ_SUCCESS)
520 return 1;
521
522 status = init_stack();
523 if (status != PJ_SUCCESS) {
524 app_perror("Error initializing stack", status);
525 return 1;
526 }
527
528 status = init_proxy();
529 if (status != PJ_SUCCESS) {
530 app_perror("Error initializing proxy", status);
531 return 1;
532 }
533
534 status = init_stateful_proxy();
535 if (status != PJ_SUCCESS) {
536 app_perror("Error initializing stateful proxy", status);
537 return 1;
538 }
539
540#if PJ_HAS_THREADS
541 status = pj_thread_create(global.pool, "sproxy", &worker_thread,
542 NULL, 0, 0, &global.thread);
543 if (status != PJ_SUCCESS) {
544 app_perror("Error creating thread", status);
545 return 1;
546 }
547
548 while (!global.quit_flag) {
549 char line[10];
550
551 puts("\n"
552 "Menu:\n"
553 " q quit\n"
554 " d dump status\n"
555 " dd dump detailed status\n"
556 "");
557
Benny Prijono32d267b2009-01-01 22:08:21 +0000558 if (fgets(line, sizeof(line), stdin) == NULL) {
559 puts("EOF while reading stdin, will quit now..");
560 global.quit_flag = PJ_TRUE;
561 break;
562 }
Benny Prijonofff245c2007-04-02 11:44:47 +0000563
564 if (line[0] == 'q') {
565 global.quit_flag = PJ_TRUE;
566 } else if (line[0] == 'd') {
567 pj_bool_t detail = (line[1] == 'd');
568 pjsip_endpt_dump(global.endpt, detail);
569 pjsip_tsx_layer_dump(detail);
570 }
571 }
572
573 pj_thread_join(global.thread);
574
575#else
576 puts("\nPress Ctrl-C to quit\n");
577 for (;;) {
578 pj_time_val delay = {0, 0};
579 pjsip_endpt_handle_events(global.endpt, &delay);
580 }
581#endif
582
583 destroy_stack();
584
585 return 0;
586}
587