/*
Copyright (C) 2005-2007 Michel de Boer <michel@twinklephone.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <assert.h>
#include <iostream>
#include <signal.h>
#include "log.h"
#include "transaction_mgr.h"
#include "sockets/url.h"
#include "util.h"
#include "audits/memman.h"
extern t_event_queue *evq_trans_mgr;
extern t_event_queue *evq_trans_layer;
extern t_event_queue *evq_timekeeper;
extern t_transaction_mgr *transaction_mgr;
t_trans_client *t_transaction_mgr::find_trans_client(t_response *r) const {
map<t_tid, t_trans_client *>::const_iterator i;
for (i = map_trans_client.begin(); i != map_trans_client.end();
i++)
{
if (i->second->match(r)) return i->second;
}
return NULL;
}
t_trans_client *t_transaction_mgr::find_trans_client(t_tid tid) const {
map<t_tid, t_trans_client *>::const_iterator i;
i = map_trans_client.find(tid);
if (i == map_trans_client.end()) return NULL;
return i->second;
}
t_trans_client *t_transaction_mgr::find_trans_client(const t_icmp_msg &icmp) const {
map<t_tid, t_trans_client *>::const_iterator i;
for (i = map_trans_client.begin(); i != map_trans_client.end();
i++)
{
if (i->second->match(icmp)) return i->second;
}
return NULL;
}
t_trans_server *t_transaction_mgr::find_trans_server(t_request *r) const {
map<t_tid, t_trans_server *>::const_iterator i;
for (i = map_trans_server.begin(); i != map_trans_server.end();
i++)
{
if (i->second->match(r)) return i->second;
}
return NULL;
}
t_trans_server *t_transaction_mgr::find_trans_server(t_tid tid) const {
map<t_tid, t_trans_server *>::const_iterator i;
i = map_trans_server.find(tid);
if (i == map_trans_server.end()) return NULL;
return i->second;
}
t_stun_transaction *t_transaction_mgr::find_stun_trans(StunMessage *r) const {
map<t_tid, t_stun_transaction *>::const_iterator i;
for (i = map_stun_trans.begin(); i != map_stun_trans.end();
i++)
{
if (i->second->match(r)) return i->second;
}
return NULL;
}
t_stun_transaction *t_transaction_mgr::find_stun_trans(t_tid tid) const {
map<t_tid, t_stun_transaction *>::const_iterator i;
i = map_stun_trans.find(tid);
if (i == map_stun_trans.end()) return NULL;
return i->second;
}
t_stun_transaction *t_transaction_mgr::find_stun_trans(const t_icmp_msg &icmp) const {
map<t_tid, t_stun_transaction *>::const_iterator i;
for (i = map_stun_trans.begin(); i != map_stun_trans.end();
i++)
{
if (i->second->match(icmp)) return i->second;
}
return NULL;
}
t_trans_server *t_transaction_mgr::find_cancel_target(t_request *r) const {
map<t_tid, t_trans_server *>::const_iterator i;
for (i = map_trans_server.begin(); i != map_trans_server.end();
i++)
{
if (i->second->match_cancel(r)) return i->second;
}
return NULL;
}
t_tc_invite *t_transaction_mgr::create_tc_invite(t_user *user_config, t_request *r,
unsigned short tuid)
{
unsigned long ipaddr;
unsigned short port;
r->get_destination(ipaddr, port, *user_config);
if (ipaddr == 0 || port == 0) return NULL;
t_tc_invite *t = new t_tc_invite(r, ipaddr, port, tuid);
MEMMAN_NEW(t);
map_trans_client[t->get_id()] = (t_trans_client *)t;
return t;
}
t_tc_non_invite *t_transaction_mgr::create_tc_non_invite(t_user *user_config, t_request *r,
unsigned short tuid)
{
unsigned long ipaddr;
unsigned short port;
r->get_destination(ipaddr, port, *user_config);
if (ipaddr == 0 || port == 0) return NULL;
t_tc_non_invite *t = new t_tc_non_invite(r, ipaddr, port, tuid);
MEMMAN_NEW(t);
map_trans_client[t->get_id()] = (t_trans_client *)t;
return t;
}
t_ts_invite *t_transaction_mgr::create_ts_invite(t_request *r) {
t_ts_invite *t = new t_ts_invite(r, 0);
MEMMAN_NEW(t);
map_trans_server[t->get_id()] = (t_trans_server *)t;
return t;
}
t_ts_non_invite *t_transaction_mgr::create_ts_non_invite(t_request *r) {
t_ts_non_invite *t = new t_ts_non_invite(r, 0);
MEMMAN_NEW(t);
map_trans_server[t->get_id()] = (t_trans_server *)t;
return t;
}
t_sip_stun_trans *t_transaction_mgr::create_sip_stun_trans(t_user *user_config, StunMessage *r,
unsigned short tuid)
{
list<t_ip_port> destinations =
user_config->get_stun_server().get_h_ip_srv("udp");
if (destinations.empty()) return NULL;
t_sip_stun_trans *t = new t_sip_stun_trans(user_config, r, tuid, destinations);
MEMMAN_NEW(t);
map_stun_trans[t->get_id()] = (t_stun_transaction *)t;
return t;
}
t_media_stun_trans *t_transaction_mgr::create_media_stun_trans(t_user *user_config,
StunMessage *r, unsigned short tuid, unsigned short src_port)
{
list<t_ip_port> destinations =
user_config->get_stun_server().get_h_ip_srv("udp");
if (destinations.empty()) return NULL;
t_media_stun_trans *t = new t_media_stun_trans(user_config, r, tuid,
destinations, src_port);
MEMMAN_NEW(t);
map_stun_trans[t->get_id()] = (t_stun_transaction *)t;
return t;
}
void t_transaction_mgr::delete_trans_client(t_trans_client *tc) {
map_trans_client.erase(tc->get_id());
MEMMAN_DELETE(tc);
delete tc;
}
void t_transaction_mgr::delete_trans_server(t_trans_server *ts) {
map_trans_server.erase(ts->get_id());
MEMMAN_DELETE(ts);
delete ts;
}
void t_transaction_mgr::delete_stun_trans(t_stun_transaction *st) {
map_stun_trans.erase(st->get_id());
MEMMAN_DELETE(st);
delete st;
}
t_transaction_mgr::~t_transaction_mgr() {
log_file->write_header("t_transaction_mgr::~t_transaction_mgr",
LOG_NORMAL, LOG_INFO);
log_file->write_raw("Clean up transaction manager.\n");
map<t_tid, t_trans_client *>::iterator i;
for (i = map_trans_client.begin(); i != map_trans_client.end();
i++)
{
log_file->write_raw("\nDeleting client transaction: \n");
log_file->write_raw("Tid: ");
log_file->write_raw(i->first);
log_file->write_raw(", Method: ");
log_file->write_raw(method2str(i->second->get_method()));
log_file->write_raw(", State: ");
log_file->write_raw(trans_state2str(i->second->get_state()));
log_file->write_endl();
MEMMAN_DELETE(i->second);
delete i->second;
}
map<t_tid, t_trans_server *>::iterator j;
for (j = map_trans_server.begin(); j != map_trans_server.end();
j++)
{
log_file->write_raw("\nDeleting server transaction: \n");
log_file->write_raw("Tid: ");
log_file->write_raw(j->first);
log_file->write_raw(", Method: ");
log_file->write_raw(method2str(j->second->get_method()));
log_file->write_raw(", State: ");
log_file->write_raw(trans_state2str(j->second->get_state()));
log_file->write_endl();
MEMMAN_DELETE(j->second);
delete j->second;
}
map<t_tid, t_stun_transaction *>::iterator k;
for (k = map_stun_trans.begin(); k != map_stun_trans.end();
k++)
{
log_file->write_raw("\nDeleting STUN transaction: \n");
log_file->write_raw("Tid: ");
log_file->write_raw(k->first);
log_file->write_raw(", State: ");
log_file->write_raw(trans_state2str(k->second->get_state()));
log_file->write_endl();
MEMMAN_DELETE(k->second);
delete k->second;
}
log_file->write_footer();
}
void t_transaction_mgr::handle_event_network(t_event_network *e) {
t_trans_server *ts;
t_ts_invite *ts_invite;
t_ts_non_invite *ts_non_invite;
t_trans_client *tc;
t_sip_message *msg = e->get_msg();
t_request *request;
t_response *response;
switch(msg->get_type()) {
case MSG_REQUEST:
// Request from network is for a server transaction
request = (t_request *)msg;
ts = find_trans_server(request);
if (ts) {
switch (request->method) {
case ACK:
// ACK for an INVITE transaction
ts_invite = (t_ts_invite *)ts;
ts_invite->acknowledge(request);
break;
default:
// A request that matches an existing
// transaction is a retransmission
ts->process_retransmission();
break;
}
if (ts->get_state() == TS_TERMINATED) {
delete_trans_server(ts);
}
return;
}
// Create a new transaction
switch (request->method) {
case INVITE:
create_ts_invite(request);
break;
case ACK:
// ACK should be passed to TU
evq_trans_layer->push_user(request, 0, 0);
break;
default:
create_ts_non_invite(request);
break;
}
break;
case MSG_RESPONSE:
// Response from network is for a client transaction
response = (t_response *)msg;
tc = find_trans_client(response);
if (!tc) {
// Only a 2XX for an INVITE transaction can be
// received while no transaction exists anymore.
// RFC 3261 17.1.1.2
if (response->is_success() &&
response->hdr_cseq.method == INVITE)
{
// Report to TU
evq_trans_layer->push_user(response, 0, 0);
} else {
log_file->write_report(
"Response does not match any transaction. Discard.",
"t_transaction_mgr::handle_event_network");
}
break;
}
tc->process_response(response);
if (tc->get_state() == TS_TERMINATED) {
delete_trans_client(tc);
}
break;
default:
assert(false);
break;
}
}
void t_transaction_mgr::handle_event_user(t_event_user *e) {
t_trans_server *ts;
t_ts_invite *ts_invite;
t_ts_non_invite *ts_non_invite;
t_sip_message *msg = e->get_msg();
t_request *request;
t_response *response;
switch(msg->get_type()) {
case MSG_REQUEST:
// A user request creates a client transaction
request = (t_request *)msg;
switch (request->method) {
case INVITE:
t_tc_invite *t1;
assert(e->get_user_config());
t1 = create_tc_invite(e->get_user_config(), request, e->get_tuid());
if (t1 == NULL) {
// Report 404 to TU
response = request->create_response(
R_404_NOT_FOUND);
log_file->write_header(
"t_transaction_mgr::handle_event_user",
LOG_NORMAL, LOG_INFO);
log_file->write_raw("Cannot resolve destination for:\n");
log_file->write_raw(request->encode());
log_file->write_endl();
log_file->write_raw("Send internal:\n");
log_file->write_raw(response->encode());
log_file->write_footer();
evq_trans_layer->push_user(response,
e->get_tuid(), 0);
MEMMAN_DELETE(response);
delete response;
}
break;
default:
t_tc_non_invite *t2;
assert(e->get_user_config());
t2 = create_tc_non_invite(e->get_user_config(), request, e->get_tuid());
if (t2 == NULL) {
// Report 404 to TU
response = request->create_response(
R_404_NOT_FOUND);
log_file->write_header(
"t_transaction_mgr::handle_event_user",
LOG_NORMAL, LOG_INFO);
log_file->write_raw("Cannot resolve destination for:\n");
log_file->write_raw(request->encode());
log_file->write_endl();
log_file->write_raw("Send internal:\n");
log_file->write_raw(response->encode());
log_file->write_footer();
evq_trans_layer->push_user(response,
e->get_tuid(), 0);
MEMMAN_DELETE(response);
delete response;
}
break;
}
break;
case MSG_RESPONSE:
// A user repsonse is for a server transaction
response = (t_response *)msg;
ts = find_trans_server(e->get_tid());
if (!ts) {
// This is an error. A response should match a
// transaction. Ignore it.
log_file->write_report(
"Response from user does not match any transaction. Ignore.",
"t_transaction_mgr::handle_event_user",
LOG_NORMAL, LOG_WARNING);
return;
}
ts->process_response(response);
if (ts->get_state() == TS_TERMINATED) {
delete_trans_server(ts);
}
break;
default:
assert(false);
break;
}
}
void t_transaction_mgr::handle_event_timeout(t_event_timeout *e) {
t_timer *t = e->get_timer();
t_tmr_transaction *tmr_trans;
t_tmr_stun_trans *tmr_stun_trans;
t_tid tid;
t_trans_client *tc;
t_trans_server *ts;
t_stun_transaction *st;
switch (t->get_type()) {
case TMR_TRANSACTION:
tmr_trans = (t_tmr_transaction *)t;
tid = tmr_trans->get_tid();
tc = find_trans_client(tid);
if (tc) {
tc->timeout(tmr_trans->get_sip_timer());
if (tc->get_state() == TS_TERMINATED) {
delete_trans_client(tc);
}
return;
}
ts = find_trans_server(tid);
if (ts) {
ts->timeout(tmr_trans->get_sip_timer());
if (ts->get_state() == TS_TERMINATED) {
delete_trans_server(ts);
}
return;
}
// The transaction is already gone. Discard timeout.
break;
case TMR_STUN_TRANSACTION:
tmr_stun_trans = (t_tmr_stun_trans *)t;
tid = tmr_stun_trans->get_tid();
st = find_stun_trans(tid);
if (st) {
st->timeout(tmr_stun_trans->get_stun_timer());
if (st->get_state() == TS_TERMINATED) {
delete_stun_trans(st);
}
return;
}
// The transaction is already gone. Discard timeout.
break;
default:
assert(false);
break;
}
}
void t_transaction_mgr::handle_event_abort(t_event_abort_trans *e) {
t_tid tid;
t_trans_client *tc;
// Only a client transaction can be aborted.
tid = e->get_tid();
tc = find_trans_client(tid);
if (tc) {
tc->abort();
if (tc->get_state() == TS_TERMINATED) {
delete_trans_client(tc);
}
}
}
void t_transaction_mgr::handle_event_stun_request(t_event_stun_request *e) {
StunMessage *msg = e->get_msg();
unsigned short tuid = e->get_tuid();
unsigned short tid = e->get_tid();
StunMessage *response;
t_sip_stun_trans *sst;
t_media_stun_trans *mst;
StunMessage *resp;
switch(e->get_stun_event_type()) {
case TYPE_STUN_SIP:
assert(e->get_user_config());
sst = create_sip_stun_trans(e->get_user_config(), msg, tuid);
if (!sst) {
// STUN server not found
log_file->write_header(
"t_transaction_mgr::handle_event_stun_request",
LOG_NORMAL, LOG_INFO);
log_file->write_raw("Cannot resolve:\n");
log_file->write_raw(e->get_user_config()->get_stun_server().encode());
log_file->write_endl();
log_file->write_raw("Send internal: 404 Not Found\n");
log_file->write_footer();
resp = stunBuildError(*msg, 404, "Not Found");
evq_trans_layer->push_stun_response(resp, tuid, tid);
MEMMAN_DELETE(resp);
delete resp;
}
break;
case TYPE_STUN_MEDIA:
assert(e->get_user_config());
mst = create_media_stun_trans(e->get_user_config(), msg, tuid, e->src_port);
if (!mst) {
// STUN server not found
log_file->write_header(
"t_transaction_mgr::handle_event_stun_request",
LOG_NORMAL, LOG_INFO);
log_file->write_raw("Cannot resolve:\n");
log_file->write_raw(e->get_user_config()->get_stun_server().encode());
log_file->write_endl();
log_file->write_raw("Send internal: 404 Not Found\n");
log_file->write_footer();
resp = stunBuildError(*msg, 404, "Not Found");
evq_trans_layer->push_stun_response(resp, tuid, tid);
MEMMAN_DELETE(resp);
delete resp;
}
break;
default:
assert(false);
break;
}
}
void t_transaction_mgr::handle_event_stun_response(t_event_stun_response *e) {
StunMessage *response = e->get_msg();
t_stun_transaction *st = find_stun_trans(response);
if (!st) {
// This response does not match any transaction.
// Ignore it.
return;
}
st->process_response(response);
if (st->get_state() == TS_TERMINATED) {
delete_stun_trans(st);
}
}
void t_transaction_mgr::handle_event_icmp(t_event_icmp *e) {
// Only a client and STUN transactions can handle ICMP errors
// If both a client and STUN transaction match then send the ICMP
// error to both transactions. It cannot be determined which transaction
// caused the error, but as both transactions have the same destination
// it is likely that both will fail.
t_trans_client *tc = find_trans_client(e->get_icmp());
if (tc) {
tc->process_icmp(e->get_icmp());
if (tc->get_state() == TS_TERMINATED) {
delete_trans_client(tc);
}
}
t_stun_transaction *st = find_stun_trans(e->get_icmp());
if (st) {
st->process_icmp(e->get_icmp());
if (st->get_state() == TS_TERMINATED) {
delete_stun_trans(st);
}
}
}
t_object_id t_transaction_mgr::start_timer(long dur, t_sip_timer tmr,
unsigned short tid)
{
t_tmr_transaction *t = new t_tmr_transaction(dur, tmr, tid);
MEMMAN_NEW(t);
evq_timekeeper->push_start_timer(t);
t_object_id timer_id = t->get_object_id();
MEMMAN_DELETE(t);
delete t;
return timer_id;
}
t_object_id t_transaction_mgr::start_stun_timer(long dur, t_stun_timer tmr,
unsigned short tid)
{
t_tmr_stun_trans *t = new t_tmr_stun_trans(dur, tmr, tid);
MEMMAN_NEW(t);
evq_timekeeper->push_start_timer(t);
t_object_id timer_id = t->get_object_id();
MEMMAN_DELETE(t);
delete t;
return timer_id;
}
void t_transaction_mgr::stop_timer(t_object_id id) {
evq_timekeeper->push_stop_timer(id);
}
void t_transaction_mgr::run(void) {
t_event *event;
t_event_network *ev_network;
t_event_user *ev_user;
t_event_timeout *ev_timeout;
t_event_abort_trans *ev_abort;
t_event_stun_request *ev_stun_request;
t_event_stun_response *ev_stun_response;
t_event_icmp *ev_icmp;
bool quit = false;
while (!quit) {
event = evq_trans_mgr->pop();
switch (event->get_type()) {
case EV_NETWORK:
ev_network = (t_event_network *)event;
handle_event_network(ev_network);
break;
case EV_USER:
ev_user = (t_event_user *)event;
handle_event_user(ev_user);
break;
case EV_TIMEOUT:
ev_timeout = (t_event_timeout *)event;
handle_event_timeout(ev_timeout);
break;
case EV_ABORT_TRANS:
ev_abort = (t_event_abort_trans *)event;
handle_event_abort(ev_abort);
break;
case EV_STUN_REQUEST:
ev_stun_request = (t_event_stun_request *)event;
handle_event_stun_request(ev_stun_request);
break;
case EV_STUN_RESPONSE:
ev_stun_response = (t_event_stun_response *)event;
handle_event_stun_response(ev_stun_response);
break;
case EV_ICMP:
ev_icmp = (t_event_icmp *)event;
handle_event_icmp(ev_icmp);
break;
case EV_QUIT:
quit = true;
break;
default:
assert(false);
break;
}
MEMMAN_DELETE(event);
delete event;
}
}
// Main function to be started in a separate thread.
void *transaction_mgr_main(void *arg) {
transaction_mgr->run();
return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1