diff options
Diffstat (limited to 'src/listener.cpp')
-rw-r--r-- | src/listener.cpp | 616 |
1 files changed, 616 insertions, 0 deletions
diff --git a/src/listener.cpp b/src/listener.cpp new file mode 100644 index 0000000..a16675d --- /dev/null +++ b/src/listener.cpp @@ -0,0 +1,616 @@ +/* + Copyright (C) 2005-2009 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 <iostream> +#include "events.h" +#include "listener.h" +#include "log.h" +#include "sys_settings.h" +#include "translator.h" +#include "user.h" +#include "userintf.h" +#include "util.h" +#include "im/im_iscomposing_body.h" +#include "sockets/connection_table.h" +#include "sockets/socket.h" +#include "parser/parse_ctrl.h" +#include "parser/sip_message.h" +#include "sdp/sdp_parse_ctrl.h" +#include "stun/stun.h" +#include "audits/memman.h" +#include "presence/pidf_body.h" + +extern t_phone *phone; +extern t_socket_udp *sip_socket; +extern t_socket_tcp *sip_socket_tcp; +extern t_connection_table *connection_table; +extern t_event_queue *evq_trans_mgr; +extern t_event_queue *evq_trans_layer; + +// Minimal size of a message. Messages below this size will +// be silently discarded. +#define MIN_MESSAGE_SIZE 10 + +// Maximum number of pending TCP connections. +#define TCP_BACKLOG 5 + +void recvd_stun_msg(char *datagram, int datagram_size, + unsigned long src_addr, unsigned short src_port) +{ + StunMessage m; + + if (!stunParseMessage(datagram, datagram_size, m, false)) { + log_file->write_report("Received faulty STUN message", + "::recvd_stun_msg", LOG_STUN, LOG_DEBUG); + return; + } + + log_file->write_header("::recvd_stun_msg", LOG_STUN); + log_file->write_raw("Received from: "); + log_file->write_raw(h_ip2str(src_addr)); + log_file->write_raw(":"); + log_file->write_raw(src_port); + log_file->write_endl(); + log_file->write_raw(stunMsg2Str(m)); + log_file->write_footer(); + + evq_trans_mgr->push_stun_response(&m, 0, 0); +} + +t_sip_body *parse_body(const string &data, const t_sip_message *msg) { + if (!msg->hdr_content_type.is_populated()) { + // Content-Type header is missing. Pass body + // unparsed. The upper application layer will + // decide what to do. + t_sip_body_opaque *p = new t_sip_body_opaque(data); + MEMMAN_NEW(p); + return p; + } + + if (msg->hdr_content_type.media.type == "application" && + msg->hdr_content_type.media.subtype == "sdp") + { + // Parse SDP body + return t_sdp_parser::parse(data); + } else if (msg->hdr_content_type.media.type == "message" && + msg->hdr_content_type.media.subtype == "sipfrag") + { + t_sip_body_sipfrag *b; + + // Parse sipfrag body (RFC 3420) + try { + // If the sipfrag does not contain a body itself, + // then the CRLF at the end of the headers is optional! + // Add an additional CRLF such that the SIP parser will + // parse a sipfrag if the CRLF is not present. The SIP + // parser will stop after it finds the double CRLF. So + // a 3rd CRLF will not be detected by the parser (yuck). + list<string> parse_errors; + t_sip_message *m = t_parser::parse(data + CRLF, parse_errors); + b = new t_sip_body_sipfrag(m); + MEMMAN_NEW(b); + MEMMAN_DELETE(m); + delete m; + return b; + } catch (int) { + // Parsing failed, maybe because a request or status + // line is not present, which is not mandatory for a + // sipfrag body. Add a fake status line and try to parse + // again. + string tmp = "SIP/2.0 100 Trying"; + tmp += CRLF; + tmp += data; + tmp += CRLF; + list<string> parse_errors; + t_sip_message *resp = t_parser::parse(tmp, parse_errors); + + // Parsing succeeded. Now strip the fake header + t_sip_message *m = new t_sip_message(*resp); + MEMMAN_NEW(m); + MEMMAN_DELETE(resp); + delete (resp); + b = new t_sip_body_sipfrag(m); + MEMMAN_NEW(b); + MEMMAN_DELETE(m); + delete m; + return b; + } + } else if (msg->hdr_content_type.media.type == "application" && + msg->hdr_content_type.media.subtype == "dtmf-relay") + { + t_sip_body_dtmf_relay *b = new t_sip_body_dtmf_relay(); + MEMMAN_NEW(b); + if (b->parse(data)) return b; + MEMMAN_DELETE(b); + delete b; + throw -1; + } else if (msg->hdr_content_type.media.type == "application" && + msg->hdr_content_type.media.subtype == "simple-message-summary") + { + t_simple_msg_sum_body *b = new t_simple_msg_sum_body(); + MEMMAN_NEW(b); + if (b->parse(data)) return b; + MEMMAN_DELETE(b); + delete b; + throw -1; + } else if (msg->hdr_content_type.media.type == "text" && + msg->hdr_content_type.media.subtype == "plain") + { + t_sip_body_plain_text *b = new t_sip_body_plain_text(data); + MEMMAN_NEW(b); + return b; + } else if (msg->hdr_content_type.media.type == "text" && + msg->hdr_content_type.media.subtype == "html") + { + t_sip_body_html_text *b = new t_sip_body_html_text(data); + MEMMAN_NEW(b); + return b; + } else if (msg->hdr_content_type.media.type == "application" && + msg->hdr_content_type.media.subtype == "pidf+xml") + { + t_pidf_xml_body *b = new t_pidf_xml_body(); + MEMMAN_NEW(b); + if (b->parse(data)) return b; + MEMMAN_DELETE(b); + delete b; + throw -1; + } else if (msg->hdr_content_type.media.type == "application" && + msg->hdr_content_type.media.subtype == "im-iscomposing+xml") + { + t_im_iscomposing_xml_body *b = new t_im_iscomposing_xml_body(); + MEMMAN_NEW(b); + if (b->parse(data)) return b; + MEMMAN_DELETE(b); + delete b; + throw -1; + } else { + // Pass other bodies unparsed. The upper application + // layer will decide what to do. + t_sip_body_opaque *p = new t_sip_body_opaque(data); + MEMMAN_NEW(p); + return p; + } +} + +static void process_sip_msg(t_sip_message *msg, const string &raw_headers, const string &raw_body) { + t_event_network *ev_network; + string log_msg; + + // SIP message received + log_msg = "Received from: "; + log_msg += msg->src_ip_port.tostring(); + log_msg += "\n"; + + // Parse body + if (!raw_body.empty()) { + // The body should only be parsed if it is complete. + // NOTE: The Content-length header may be absent (UDP) + if (!msg->hdr_content_length.is_populated() || + msg->hdr_content_length.length == raw_body.size()) + { + try { + msg->body = parse_body(raw_body, msg); + } + catch (int) { + if (msg->get_type() == MSG_RESPONSE) { + // Discard a SIP response if the body is malformed. + log_msg += "Invalid SIP message.\n"; + log_msg += "Parse error in body.\n"; + log_msg += to_printable(raw_headers); + log_msg += to_printable(raw_body); + log_file->write_report(log_msg, "::process_sip_msg", + LOG_SIP, LOG_DEBUG); + + return; + } else { + // For a SIP request with a malformed body, the + // transaction layer will give an error response. + // Set the invalid body indication for the transaction + // layer. + msg->body = new t_sip_body_opaque(); + MEMMAN_NEW(msg->body); + msg->body->invalid = true; + } + } + } else { + log_file->write_report("Received incomplete body", "::process_sip_msg", + LOG_NORMAL, LOG_WARNING); + } + } + + log_msg += to_printable(raw_headers); + log_msg += to_printable(raw_body); + log_file->write_report(log_msg, "::process_sip_msg", LOG_SIP); + + // If the message does not satisfy the mandatory + // requirements from RFC 3261, then discard. + // If the error is non-fatal, then the transaction layer + // will send a proper error response. + // If the message is an invalid response message then + // discard the message. The transaction layer cannot + // handle an invalid response as it cannot send an + // error message back on an answer. + bool fatal; + string reason; + if (!msg->is_valid(fatal, reason) && + (fatal || msg->get_type() == MSG_RESPONSE)) + { + log_file->write_header("::process_sip_msg", LOG_SIP); + log_file->write_raw("Discard invalid message.\n"); + log_file->write_raw(reason); + log_file->write_endl(); + log_file->write_footer(); + + return; + } + + if (msg->get_type() == MSG_REQUEST) { + // RFC 3261 18.2.1 + // When the server transport receives a request over any transport, it + // MUST examine the value of the "sent-by" parameter in the top Via + // header field value. If the host portion of the "sent-by" parameter + // contains a domain name, or if it contains an IP address that differs + // from the packet source address, the server MUST add a "received" + // parameter to that Via header field value. This parameter MUST + // contain the source address from which the packet was received. + string src_ip = h_ip2str(msg->src_ip_port.ipaddr); + t_via &top_via = msg->hdr_via.via_list.front(); + if (top_via.host != src_ip) { + top_via.received = src_ip; + log_file->write_header("::process_sip_msg", LOG_SIP); + log_file->write_raw("Added via-parameter received="); + log_file->write_raw(src_ip); + log_file->write_endl(); + log_file->write_footer(); + } + + // RFC 3581 4 + // Add rport value if requested + // Add received parameter + if (top_via.rport_present && top_via.rport == 0) { + top_via.rport = msg->src_ip_port.port; + top_via.received = src_ip; + } + } + + ev_network = new t_event_network(msg); + MEMMAN_NEW(ev_network); + ev_network->src_addr = msg->src_ip_port.ipaddr; + ev_network->src_port = msg->src_ip_port.port; + ev_network->transport = msg->src_ip_port.transport; + evq_trans_mgr->push(ev_network); +} + +void *listen_udp(void *arg) { + char buf[sys_config->get_sip_max_udp_size() + 1]; + int data_size; + unsigned long src_addr; + unsigned short src_port; + t_sip_message *msg; + t_event_icmp *ev_icmp; + string::size_type pos_body; // position of body in msg + string log_msg; + + // Number of consecutive non-icmp errors received + int num_non_icmp_errors = 0; + + while(true) { + try { + data_size = sip_socket->recvfrom(src_addr, src_port, buf, + sys_config->get_sip_max_udp_size() + 1); + num_non_icmp_errors = 0; + } catch (int err) { + // 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, "::listen_udp", LOG_NORMAL); + + ev_icmp = new t_event_icmp(icmp); + MEMMAN_NEW(ev_icmp); + evq_trans_mgr->push(ev_icmp); + + num_non_icmp_errors = 0; + } 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 receive from 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, "::listen_udp"); + + 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, "::listen_udp", + LOG_NORMAL, LOG_CRITICAL); + log_msg = TRANSLATE("Excessive number of socket errors."); + ui->cb_show_msg(log_msg, MSG_CRITICAL); + exit(1); + } + */ + } + + continue; + } + + // Some SIP proxies send small keep alive packets to keep + // NAT bindings open. Discard such small packets as these + // are not SIP or STUN messages. + if (data_size < MIN_MESSAGE_SIZE) continue; + + // Check if this is a STUN message + // The first byte of a STUN message is 0x00 or 0x01. + // A SIP message is ASCII so the first byte for SIP is + // never 0x00 or 0x01 + if (buf[0] <= 1) + { + recvd_stun_msg(buf, data_size, src_addr, src_port); + continue; + } + + // A SIP message may contain a NULL character (binary body), + // do not handle the buffer as a C string. + string datagram(buf, data_size); + + // Split body from header + string seperator = string(CRLF) + string(CRLF); + pos_body = datagram.find(seperator); + + // According to RFC 3261 syntax an empty line at + // the end of the headers is mandatory in all SIP messages. + // Here a missing empty line is accepted, but maybe + // the message should be discarded. + if (pos_body != string::npos) { + pos_body += seperator.size(); + if (pos_body >= datagram.size()) { + // No body is present + pos_body = string::npos; + } + } + + // Parse SIP headers + string raw_headers = datagram.substr(0, pos_body); + list<string> parse_errors; + try { + msg = t_parser::parse(raw_headers, parse_errors); + msg->src_ip_port.ipaddr = src_addr; + msg->src_ip_port.port = src_port; + msg->src_ip_port.transport = "udp"; + } + catch (int) { + // Discard malformed SIP messages. + log_msg = "Invalid SIP message.\n"; + log_msg += "Fatal parse error in headers.\n\n"; + log_msg += to_printable(datagram); + log_msg += "\n"; + log_file->write_report(log_msg, "::listen_udp", LOG_SIP, LOG_DEBUG); + continue; + } + + // Log non-fatal parse errors. + if (!parse_errors.empty()) { + log_msg = "Parse errors:\n"; + log_msg += "\n"; + for (list<string>::iterator i = parse_errors.begin(); + i != parse_errors.end(); i++) + { + log_msg += *i; + log_msg += "\n"; + } + log_msg += "\n"; + log_file->write_report(log_msg, "::listen_udp", LOG_SIP, LOG_DEBUG); + } + + // Get raw body + string raw_body; + if (pos_body != string::npos) { + raw_body = datagram.substr(pos_body); + } + + process_sip_msg(msg, raw_headers, raw_body); + + MEMMAN_DELETE(msg); + delete msg; + } + + log_file->write_report("UDP listener terminated.", "::listen_udp"); + return NULL; +} + +void *listen_for_data_tcp(void *arg) { + string log_msg; + list<t_connection *> readable_connections; + + while(true) { + readable_connections.clear(); + readable_connections = connection_table->select_read(NULL); + + if (readable_connections.empty()) { + // Another thread cancelled the select command. + // Stop listening. + break; + } + + // NOTE: The connection table is now locked. + + for (list<t_connection *>::iterator it = readable_connections.begin(); + it != readable_connections.end(); ++it) + { + string raw_headers; + string raw_body; + unsigned long remote_addr; + unsigned short remote_port; + + (*it)->get_remote_address(remote_addr, remote_port); + + try { + bool connection_closed; + (*it)->read(connection_closed); + + if (connection_closed) { + log_msg = "Connection to "; + log_msg += h_ip2str(remote_addr); + log_msg += ":"; + log_msg += int2str(remote_port); + log_msg += " closed."; + log_file->write_report(log_msg, "::listen_for_data_tcp", LOG_SIP, LOG_DEBUG); + + connection_table->remove_connection(*it); + MEMMAN_DELETE(*it); + delete *it; + continue; + } + } catch (int err) { + if (err == EAGAIN || err == EINTR) { + continue; + } + + log_msg = "Got error on socket to "; + log_msg += h_ip2str(remote_addr); + log_msg += ":"; + log_msg += int2str(remote_port); + log_msg += " - "; + log_msg += get_error_str(err); + log_file->write_report(log_msg, "::listen_for_data_tcp", LOG_SIP, LOG_WARNING); + + // Connection is broken. + // Signal the transaction layer that the connection is broken for + // all associated registered URI's. + const list<t_url> &uris = (*it)->get_registered_uri_set(); + for (list<t_url>::const_iterator it_uri = uris.begin(); + it_uri != uris.end(); ++it_uri) + { + evq_trans_layer->push_broken_connection(*it_uri); + } + + // Remove the broken connection. + connection_table->remove_connection(*it); + MEMMAN_DELETE(*it); + delete *it; + + continue; + } + + // Multiple messages may have been read in one action. + // Get all SIP messages from the connection. + while (true) + { + bool error = false; + bool msg_too_large = false; + t_sip_message *msg = (*it)->get_sip_msg(raw_headers, raw_body, error, + msg_too_large); + + if (error) { + // The data on the connection could not be interpreted. + // Close the connection to a faulty remote end. + connection_table->remove_connection(*it); + MEMMAN_DELETE(*it); + delete *it; + + break; + } + + if (msg_too_large) { + // Close the connection. The message was too long, we don't want + // to receive more data. Now we have an incomplete message, + // but as we most likely have all headers we can still + // send an error response, so the message is processed. + connection_table->remove_connection(*it); + MEMMAN_DELETE(*it); + delete *it; + } + + if (!msg) { + // There are no complete messages on the connection. + // Stop reading from this connection. + break; + } + + process_sip_msg(msg, raw_headers, raw_body); + + MEMMAN_DELETE(msg); + delete msg; + + if (msg_too_large) { + // The connection is closed already. Stop reading. + break; + } + } + } + + connection_table->unlock(); + } + + log_file->write_report("TCP data listener terminated.", "::listen_for_data_tcp"); + + return NULL; +} + +void *listen_for_conn_requests_tcp(void *arg) { + unsigned long dst_addr; + unsigned short dst_port; + string log_msg; + + while (true) { + try { + sip_socket_tcp->listen(TCP_BACKLOG); + t_socket_tcp *tcp = sip_socket_tcp->accept(dst_addr, dst_port); + t_connection *conn = new t_connection(tcp); + MEMMAN_NEW(conn); + connection_table->add_connection(conn); + } catch (int err) { + if (err == EAGAIN || err == EINTR) continue; + + log_file->write_header("::listen_for_conn_requests_tcp", LOG_SIP, LOG_CRITICAL); + log_file->write_raw("Error on accept on TCP socket: "); + log_file->write_raw(get_error_str(err)); + log_file->write_endl(); + log_file->write_footer(); + + log_msg = TRANSLATE("Cannot receive incoming TCP connections."); + ui->cb_show_msg(log_msg, MSG_CRITICAL); + + break; + } + } + + log_file->write_report("TCP connection listener terminated.", "::listen_for_conn_requests_tcp"); + return NULL; +} |