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