1
/* $Id: stateful_proxy.c 3553 2011-05-05 06:14:19Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
20
#define THIS_FILE "stateful_proxy.c"
22
/* Common proxy functions */
28
* mod_stateful_proxy is the module to receive SIP request and
29
* response message that is outside any transaction context.
31
static pj_bool_t proxy_on_rx_request(pjsip_rx_data *rdata );
32
static pj_bool_t proxy_on_rx_response(pjsip_rx_data *rdata );
34
static pjsip_module mod_stateful_proxy =
36
NULL, NULL, /* prev, next. */
37
{ "mod-stateful-proxy", 18 }, /* Name. */
39
PJSIP_MOD_PRIORITY_UA_PROXY_LAYER, /* Priority */
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() */
53
* mod_tu (tu=Transaction User) is the module to receive notification
54
* from transaction when the transaction state has changed.
56
static void tu_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event);
58
static pjsip_module mod_tu =
60
NULL, NULL, /* prev, next. */
61
{ "mod-transaction-user", 20 }, /* Name. */
63
PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
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() */
76
/* This is the data that is attached to the UAC transaction */
79
pjsip_transaction *uas_tsx;
84
/* This is the data that is attached to the UAS transaction */
87
pjsip_transaction *uac_tsx;
92
static pj_status_t init_stateful_proxy(void)
96
status = pjsip_endpt_register_module( global.endpt, &mod_stateful_proxy);
97
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
99
status = pjsip_endpt_register_module( global.endpt, &mod_tu);
100
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
106
/* Callback to be called to handle new incoming requests. */
107
static pj_bool_t proxy_on_rx_request( pjsip_rx_data *rdata )
109
pjsip_transaction *uas_tsx, *uac_tsx;
110
struct uac_data *uac_data;
111
struct uas_data *uas_data;
112
pjsip_tx_data *tdata;
115
if (rdata->msg_info.msg->line.req.method.id != PJSIP_CANCEL_METHOD) {
117
/* Verify incoming request */
118
status = proxy_verify_request(rdata);
119
if (status != PJ_SUCCESS) {
120
app_perror("RX invalid request", status);
125
* Request looks sane, next clone the request to create transmit data.
127
status = pjsip_endpt_create_request_fwd(global.endpt, rdata, NULL,
129
if (status != PJ_SUCCESS) {
130
pjsip_endpt_respond_stateless(global.endpt, rdata,
131
PJSIP_SC_INTERNAL_SERVER_ERROR,
137
/* Process routing */
138
status = proxy_process_routing(tdata);
139
if (status != PJ_SUCCESS) {
140
app_perror("Error processing route", status);
144
/* Calculate target */
145
status = proxy_calculate_target(rdata, tdata);
146
if (status != PJ_SUCCESS) {
147
app_perror("Error calculating target", status);
151
/* Everything is set to forward the request. */
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.
159
if (tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) {
160
status = pjsip_endpt_send_request_stateless(global.endpt, tdata,
162
if (status != PJ_SUCCESS) {
163
app_perror("Error forwarding request", status);
170
/* Create UAC transaction for forwarding the request.
171
* Set our module as the transaction user to receive further
172
* events from this transaction.
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,
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,
190
pjsip_tsx_terminate(uac_tsx, PJSIP_SC_INTERNAL_SERVER_ERROR);
194
/* Feed the request to the UAS transaction to drive it's state
197
pjsip_tsx_recv_msg(uas_tsx, rdata);
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.
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;
207
/* Attach data to the UAS transaction, to find the UAC transaction
208
* when cancelling INVITE request.
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;
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;
220
/* Fail to send request, for some reason */
222
/* Destroy transmit data */
223
pjsip_tx_data_dec_ref(tdata);
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);
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);
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;
242
pjsip_endpt_create_response(global.endpt, rdata, 100, NULL,
244
pjsip_tsx_send_msg(uas_tsx, res100);
248
/* This is CANCEL request */
249
pjsip_transaction *invite_uas;
250
struct uas_data *uas_data;
253
/* Find the UAS INVITE transaction */
254
pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_UAS_ROLE,
255
pjsip_get_invite_method(), rdata);
256
invite_uas = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE);
258
/* Invite transaction not found, respond CANCEL with 481 */
259
pjsip_endpt_respond_stateless(global.endpt, rdata, 481, NULL,
264
/* Respond 200 OK to CANCEL */
265
pjsip_endpt_respond(global.endpt, NULL, rdata, 200, NULL, NULL,
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.
272
uas_data = (struct uas_data*) invite_uas->mod_data[mod_tu.id];
273
if (uas_data->uac_tsx && uas_data->uac_tsx->status_code < 200) {
274
pjsip_tx_data *cancel;
276
pj_mutex_lock(uas_data->uac_tsx->mutex);
278
pjsip_endpt_create_cancel(global.endpt, uas_data->uac_tsx->last_tx,
280
pjsip_endpt_send_request(global.endpt, cancel, -1, NULL, NULL);
282
pj_mutex_unlock(uas_data->uac_tsx->mutex);
285
/* Unlock UAS tsx because it is locked in find_tsx() */
286
pj_mutex_unlock(invite_uas->mutex);
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.
299
static pj_bool_t proxy_on_rx_response( pjsip_rx_data *rdata )
301
pjsip_tx_data *tdata;
302
pjsip_response_addr res_addr;
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);
313
/* Get topmost Via header */
314
hvia = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
316
/* Invalid response! Just drop it */
317
pjsip_tx_data_dec_ref(tdata);
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);
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;
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;
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.
342
res_addr.dst_host.addr.port = hvia->sent_by.port;
345
/* Forward response */
346
status = pjsip_endpt_send_response(global.endpt, &res_addr, tdata,
348
if (status != PJ_SUCCESS) {
349
app_perror("Error forwarding response", status);
357
/* Callback to be called to handle transaction state changed. */
358
static void tu_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
360
struct uac_data *uac_data;
363
if (tsx->role == PJSIP_ROLE_UAS) {
364
if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
365
struct uas_data *uas_data;
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;
378
/* Get the data that we attached to the UAC transaction previously */
379
uac_data = (struct uac_data*) tsx->mod_data[mod_tu.id];
382
/* Handle incoming response */
383
if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
385
pjsip_rx_data *rdata;
386
pjsip_response_addr res_addr;
388
pjsip_tx_data *tdata;
390
rdata = event->body.tsx_state.src.rdata;
392
/* Do not forward 100 response for INVITE (we already responded
395
if (tsx->method.id == PJSIP_INVITE_METHOD &&
396
rdata->msg_info.msg->line.status.code == 100)
401
/* Create response to be forwarded upstream
402
* (Via will be stripped here)
404
status = pjsip_endpt_create_response_fwd(global.endpt, rdata, 0,
406
if (status != PJ_SUCCESS) {
407
app_perror("Error creating response", status);
411
/* Get topmost Via header of the new response */
412
hvia = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA,
415
/* Invalid response! Just drop it */
416
pjsip_tx_data_dec_ref(tdata);
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);
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;
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;
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.
441
res_addr.dst_host.addr.port = hvia->sent_by.port;
444
/* Forward response with the UAS transaction */
445
pjsip_tsx_send_msg(uac_data->uas_tsx, tdata);
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
454
if (tsx->state == PJSIP_TSX_STATE_TERMINATED && uac_data &&
458
pjsip_transaction *uas_tsx;
459
struct uas_data *uas_data;
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;
465
if (event->body.tsx_state.type == PJSIP_EVENT_TIMER) {
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.
471
if (tsx->method.id == PJSIP_INVITE_METHOD) {
473
pjsip_tx_data *tdata = uas_tsx->last_tx;
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;
479
pjsip_tx_data_add_ref(tdata);
480
pjsip_tx_data_invalidate_msg(tdata);
482
pjsip_tsx_send_msg(uas_tsx, tdata);
485
/* For non-INVITE, just destroy the UAS transaction */
486
pjsip_tsx_terminate(uas_tsx, PJSIP_SC_REQUEST_TIMEOUT);
489
} else if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
491
if (uas_tsx->state < PJSIP_TSX_STATE_TERMINATED) {
495
msg = event->body.tsx_state.src.rdata->msg_info.msg;
496
code = msg->line.status.code;
498
uac_data->uas_tsx = NULL;
499
pjsip_tsx_terminate(uas_tsx, code);
509
int main(int argc, char *argv[])
514
global.record_route = 0;
518
status = init_options(argc, argv);
519
if (status != PJ_SUCCESS)
522
status = init_stack();
523
if (status != PJ_SUCCESS) {
524
app_perror("Error initializing stack", status);
528
status = init_proxy();
529
if (status != PJ_SUCCESS) {
530
app_perror("Error initializing proxy", status);
534
status = init_stateful_proxy();
535
if (status != PJ_SUCCESS) {
536
app_perror("Error initializing stateful proxy", status);
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);
548
while (!global.quit_flag) {
555
" dd dump detailed status\n"
558
if (fgets(line, sizeof(line), stdin) == NULL) {
559
puts("EOF while reading stdin, will quit now..");
560
global.quit_flag = PJ_TRUE;
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);
573
pj_thread_join(global.thread);
576
puts("\nPress Ctrl-C to quit\n");
578
pj_time_val delay = {0, 0};
579
pjsip_endpt_handle_events(global.endpt, &delay);