/*
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 <ctime>
#include "events.h"
#include "log.h"
#include "sender.h"
#include "translator.h"
#include "userintf.h"
#include "util.h"
#include "sockets/socket.h"
#include "parser/parse_ctrl.h"
#include "parser/sip_message.h"
#include "audits/memman.h"
#include "stun/stun.h"
#define MAX_TRANSMIT_RETRIES 3
extern t_socket_udp *sip_socket;
extern t_event_queue *evq_sender_udp;
extern t_event_queue *evq_trans_mgr;
// Number of consecutive non-icmp errors received
static int num_non_icmp_errors = 0;
// Check if the error is caused by an incoming ICMP error. If so, then deliver
// the ICMP error to the transaction manager.
//
// err - error returned by sendto
// dst_addr - destination IP address of packet that failed to be sent
// dst_port - destination port of packet that failed to be sent
//
// Returns true if the packet that failed to be sent, should still be sent.
// Returns false if the packet that failed to be sent, should be discarded.
static bool handle_socket_err(int err, unsigned long dst_addr, unsigned short dst_port) {
t_event_icmp *ev_icmp;
string log_msg;
// Check if an ICMP error has been received
t_icmp_msg icmp;
if (sip_socket->get_icmp(icmp)) {
log_msg = "Received ICMP from: ";
log_msg += h_ip2str(icmp.icmp_src_ipaddr);
log_msg += "\nICMP type: ";
log_msg += int2str(icmp.type);
log_msg += "\nICMP code: ";
log_msg += int2str(icmp.code);
log_msg += "\nDestination of packet causing ICMP: ";
log_msg += h_ip2str(icmp.ipaddr);
log_msg += ":";
log_msg += int2str(icmp.port);
log_msg += "\nSocket error: ";
log_msg += int2str(err);
log_msg += " ";
log_msg += get_error_str(err);
log_file->write_report(log_msg, "::hanlde_socket_err", LOG_NORMAL);
ev_icmp = new t_event_icmp(icmp);
MEMMAN_NEW(ev_icmp);
evq_trans_mgr->push(ev_icmp);
num_non_icmp_errors = 0;
// If the ICMP error comes from the same destination as the
// destination of the packet that failed to be sent, then the
// packet should be discarded as it can most likely not be
// delivered and would cause an infinite loop of ICMP errors
// otherwise.
if (icmp.ipaddr == dst_addr && icmp.port == dst_port) {
return false;
}
} else {
// Even if an ICMP message is received this code can get executed.
// Sometimes the error is already present on the socket, but the ICMP
// message is not yet queued.
log_msg = "Failed to send to SIP UDP socket.\n";
log_msg += "Error code: ";
log_msg += int2str(err);
log_msg += "\n";
log_msg += get_error_str(err);
log_file->write_report(log_msg, "::handle_socket_err");
num_non_icmp_errors++;
/*
* non-ICMP errors occur when a destination on the same
* subnet cannot be reached. So this code seems to be
* harmful.
if (num_non_icmp_errors > 100) {
log_msg = "Excessive number of socket errors.";
log_file->write_report(log_msg, "::handle_socket_err",
LOG_NORMAL, LOG_CRITICAL);
log_msg = TRANSLATE("Excessive number of socket errors.");
ui->cb_show_msg(log_msg, MSG_CRITICAL);
exit(1);
}
*/
}
return true;
}
static void send_sip_udp(t_event *event) {
t_event_network *e;
e = (t_event_network *)event;
assert(e->dst_addr != 0);
assert(e->dst_port != 0);
string m = e->get_msg()->encode();
log_file->write_header("::send_sip_udp", LOG_SIP);
log_file->write_raw("Send to: ");
log_file->write_raw(h_ip2str(e->dst_addr));
log_file->write_raw(":");
log_file->write_raw(e->dst_port);
log_file->write_endl();
log_file->write_raw(m);
log_file->write_endl();
log_file->write_footer();
bool msg_sent = false;
int transmit_count = 0;
while (!msg_sent && transmit_count++ <= MAX_TRANSMIT_RETRIES) {
try {
sip_socket->sendto(e->dst_addr, e->dst_port, m.c_str(), m.size());
num_non_icmp_errors = 0;
msg_sent = true;
} catch (int err) {
if (!handle_socket_err(err, e->dst_addr, e->dst_port)) {
// Discard packet.
msg_sent = true;
} else {
if (transmit_count <= MAX_TRANSMIT_RETRIES) {
// Sleep 100 ms
struct timespec sleeptimer;
sleeptimer.tv_sec = 0;
sleeptimer.tv_nsec = 100000000;
nanosleep(&sleeptimer, NULL);
}
}
}
}
}
static void send_stun(t_event *event) {
t_event_stun_request *e;
e = (t_event_stun_request *)event;
assert(e->dst_addr != 0);
assert(e->dst_port != 0);
log_file->write_header("::send_stun", LOG_STUN);
log_file->write_raw("Send to: ");
log_file->write_raw(h_ip2str(e->dst_addr));
log_file->write_raw(":");
log_file->write_raw(e->dst_port);
log_file->write_endl();
log_file->write_raw(stunMsg2Str(*e->get_msg()));
log_file->write_footer();
StunAtrString stun_pass;
stun_pass.sizeValue = 0;
char m[STUN_MAX_MESSAGE_SIZE];
int msg_size = stunEncodeMessage(*e->get_msg(), m,
STUN_MAX_MESSAGE_SIZE, stun_pass, false);
bool msg_sent = false;
int transmit_count = 0;
while (!msg_sent && transmit_count++ <= MAX_TRANSMIT_RETRIES) {
try {
sip_socket->sendto(e->dst_addr, e->dst_port, m, msg_size);
num_non_icmp_errors = 0;
msg_sent = true;
} catch (int err) {
if (!handle_socket_err(err, e->dst_addr, e->dst_port)) {
// Discard packet.
msg_sent = true;
} else {
if (transmit_count <= MAX_TRANSMIT_RETRIES) {
// Sleep 100 ms
struct timespec sleeptimer;
sleeptimer.tv_sec = 0;
sleeptimer.tv_nsec = 100000000;
nanosleep(&sleeptimer, NULL);
}
}
}
}
}
static void send_nat_keepalive(t_event *event) {
t_event_nat_keepalive *e;
e = (t_event_nat_keepalive *)event;
assert(e->dst_addr != 0);
assert(e->dst_port != 0);
char m[2] = { '\r', '\n' };
bool msg_sent = false;
int transmit_count = 0;
while (!msg_sent && transmit_count++ <= MAX_TRANSMIT_RETRIES) {
try {
sip_socket->sendto(e->dst_addr, e->dst_port, m, 2);
num_non_icmp_errors = 0;
msg_sent = true;
} catch (int err) {
if (!handle_socket_err(err, e->dst_addr, e->dst_port)) {
// Discard packet.
msg_sent = true;
} else {
if (transmit_count <= MAX_TRANSMIT_RETRIES) {
// Sleep 100 ms
struct timespec sleeptimer;
sleeptimer.tv_sec = 0;
sleeptimer.tv_nsec = 100000000;
nanosleep(&sleeptimer, NULL);
}
}
}
}
}
void *sender_udp(void *arg) {
t_event *event;
bool quit = false;
while (!quit) {
event = evq_sender_udp->pop();
switch(event->get_type()) {
case EV_NETWORK:
send_sip_udp(event);
break;
case EV_STUN_REQUEST:
send_stun(event);
break;
case EV_NAT_KEEPALIVE:
send_nat_keepalive(event);
break;
case EV_QUIT:
quit = true;
break;
default:
assert(false);
}
MEMMAN_DELETE(event);
delete event;
}
}
syntax highlighted by Code2HTML, v. 0.9.1