/* Copyright (C) 2005-2009 Michel de Boer 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, see . */ #include #include #include #include "sip_message.h" #include "util.h" #include "parse_ctrl.h" #include "sdp/sdp.h" #include "audits/memman.h" //////////////////////////////////// // class t_sip_message //////////////////////////////////// t_sip_message::t_sip_message() : local_ip_(0) { src_ip_port.clear(); version = SIP_VERSION; body = NULL; } t_sip_message::t_sip_message(const t_sip_message& m) : local_ip_(m.local_ip_), src_ip_port(m.src_ip_port), version(m.version), hdr_accept(m.hdr_accept), hdr_accept_encoding(m.hdr_accept_encoding), hdr_accept_language(m.hdr_accept_language), hdr_alert_info(m.hdr_alert_info), hdr_allow(m.hdr_allow), hdr_allow_events(m.hdr_allow_events), hdr_auth_info(m.hdr_auth_info), hdr_authorization(m.hdr_authorization), hdr_call_id(m.hdr_call_id), hdr_call_info(m.hdr_call_info), hdr_contact(m.hdr_contact), hdr_content_disp(m.hdr_content_disp), hdr_content_encoding(m.hdr_content_encoding), hdr_content_language(m.hdr_content_language), hdr_content_length(m.hdr_content_length), hdr_content_type(m.hdr_content_type), hdr_cseq(m.hdr_cseq), hdr_date(m.hdr_date), hdr_error_info(m.hdr_error_info), hdr_event(m.hdr_event), hdr_expires(m.hdr_expires), hdr_from(m.hdr_from), hdr_in_reply_to(m.hdr_in_reply_to), hdr_max_forwards(m.hdr_max_forwards), hdr_min_expires(m.hdr_min_expires), hdr_mime_version(m.hdr_mime_version), hdr_organization(m.hdr_organization), hdr_p_asserted_identity(m.hdr_p_asserted_identity), hdr_p_preferred_identity(m.hdr_p_preferred_identity), hdr_priority(m.hdr_priority), hdr_privacy(m.hdr_privacy), hdr_proxy_authenticate(m.hdr_proxy_authenticate), hdr_proxy_authorization(m.hdr_proxy_authorization), hdr_proxy_require(m.hdr_proxy_require), hdr_rack(m.hdr_rack), hdr_record_route(m.hdr_record_route), hdr_refer_sub(m.hdr_refer_sub), hdr_refer_to(m.hdr_refer_to), hdr_referred_by(m.hdr_referred_by), hdr_replaces(m.hdr_replaces), hdr_reply_to(m.hdr_reply_to), hdr_require(m.hdr_require), hdr_request_disposition(m.hdr_request_disposition), hdr_retry_after(m.hdr_retry_after), hdr_route(m.hdr_route), hdr_rseq(m.hdr_rseq), hdr_server(m.hdr_server), hdr_service_route(m.hdr_service_route), hdr_sip_etag(m.hdr_sip_etag), hdr_sip_if_match(m.hdr_sip_if_match), hdr_subject(m.hdr_subject), hdr_subscription_state(m.hdr_subscription_state), hdr_supported(m.hdr_supported), hdr_timestamp(m.hdr_timestamp), hdr_to(m.hdr_to), hdr_unsupported(m.hdr_unsupported), hdr_user_agent(m.hdr_user_agent), hdr_via(m.hdr_via), hdr_warning(m.hdr_warning), hdr_www_authenticate(m.hdr_www_authenticate), unknown_headers(m.unknown_headers) { if (m.body) { body = m.body->copy(); } else { body = NULL; } } t_sip_message::~t_sip_message() { if (body) { MEMMAN_DELETE(body); delete body; } } t_msg_type t_sip_message::get_type(void) const { return MSG_SIPFRAG; } void t_sip_message::add_unknown_header(const string &name, const string &value) { t_parameter h(name, value); unknown_headers.push_back(h); } bool t_sip_message::is_valid(bool &fatal, string &reason) const { // RFC 3261 8.1.1 // Mandatory headers if (!hdr_to.is_populated()) { fatal = true; reason = "To-header missing"; return false; } if (!hdr_from.is_populated()) { fatal = true; reason = "From header missing"; return false; } if (!hdr_cseq.is_populated()) { fatal = true; reason = "CSeq header missing"; return false; } if (!hdr_call_id.is_populated()) { fatal = true; reason = "Call-ID header missing"; return false; } if (!hdr_via.is_populated()) { fatal = true; reason = "Via header missing"; return false; } // RFC 3261 20.15 // Content-Type MUST be present if body is not empty if (body && !hdr_content_type.is_populated()) { fatal = false; reason = "Content-Type header missing"; return false; } // RFC 3261 18.4 // The Content-Length header field MUST be used with stream oriented transports. if (cmp_nocase(hdr_via.via_list.front().transport, "tcp") == 0 && !hdr_content_length.is_populated()) { fatal = false; reason = "Content-Length header missing"; return false; } return true; } string t_sip_message::encode(bool add_content_length) { string s; string encoded_body; // RFC 3261 7.3.1 // Headers needed by a proxy should be on top s += hdr_via.encode(); s += hdr_route.encode(); s += hdr_record_route.encode(); s += hdr_service_route.encode(); s += hdr_proxy_require.encode(); s += hdr_max_forwards.encode(); s += hdr_proxy_authenticate.encode(); s += hdr_proxy_authorization.encode(); // Order as in many examples s += hdr_to.encode(); s += hdr_from.encode(); s += hdr_call_id.encode(); s += hdr_cseq.encode(); s += hdr_contact.encode(); s += hdr_content_type.encode(); // Privacy related headers s += hdr_privacy.encode(); s += hdr_p_asserted_identity.encode(); s += hdr_p_preferred_identity.encode(); // Authentication headers s += hdr_auth_info.encode(); s += hdr_authorization.encode(); s += hdr_www_authenticate.encode(); // Remaining headers in alphabetical order s += hdr_accept.encode(); s += hdr_accept_encoding.encode(); s += hdr_accept_language.encode(); s += hdr_alert_info.encode(); s += hdr_allow.encode(); s += hdr_allow_events.encode(); s += hdr_call_info.encode(); s += hdr_content_disp.encode(); s += hdr_content_encoding.encode(); s += hdr_content_language.encode(); s += hdr_date.encode(); s += hdr_error_info.encode(); s += hdr_event.encode(); s += hdr_expires.encode(); s += hdr_in_reply_to.encode(); s += hdr_min_expires.encode(); s += hdr_mime_version.encode(); s += hdr_organization.encode(); s += hdr_priority.encode(); s += hdr_rack.encode(); s += hdr_refer_sub.encode(); s += hdr_refer_to.encode(); s += hdr_referred_by.encode(); s += hdr_replaces.encode(); s += hdr_reply_to.encode(); s += hdr_require.encode(); s += hdr_request_disposition.encode(); s += hdr_retry_after.encode(); s += hdr_rseq.encode(); s += hdr_server.encode(); s += hdr_sip_etag.encode(); s += hdr_sip_if_match.encode(); s += hdr_subject.encode(); s += hdr_subscription_state.encode(); s += hdr_supported.encode(); s += hdr_timestamp.encode(); s += hdr_unsupported.encode(); s += hdr_user_agent.encode(); s += hdr_warning.encode(); // Unknown headers for (list::const_iterator i = unknown_headers.begin(); i != unknown_headers.end(); i++) { s += i->name; s += ": "; s += i->value; s += CRLF; } // Encode body if present. Set the content length. if (body) { encoded_body = body->encode(); hdr_content_length.set_length(encoded_body.size()); } else { // RFC 3261 20.14 // If no body is present then Content-Length MUST be 0 hdr_content_length.set_length(0); } // Content-Length appears last in examples if (add_content_length) { s += hdr_content_length.encode(); } // Blank line between headers and body s += CRLF; // Add body if (body) s += encoded_body; return s; } list t_sip_message::encode_env(void) { list l; // RFC 3261 7.3.1 // Headers needed by a proxy should be on top l.push_back(hdr_via.encode_env()); l.push_back(hdr_route.encode_env()); l.push_back(hdr_record_route.encode_env()); l.push_back(hdr_service_route.encode_env()); l.push_back(hdr_proxy_require.encode_env()); l.push_back(hdr_max_forwards.encode_env()); l.push_back(hdr_proxy_authenticate.encode_env()); l.push_back(hdr_proxy_authorization.encode_env()); // Order as in many examples l.push_back(hdr_to.encode_env()); l.push_back(hdr_from.encode_env()); l.push_back(hdr_call_id.encode_env()); l.push_back(hdr_cseq.encode_env()); l.push_back(hdr_contact.encode_env()); l.push_back(hdr_content_type.encode_env()); // Authentication headers l.push_back(hdr_auth_info.encode_env()); l.push_back(hdr_authorization.encode_env()); l.push_back(hdr_www_authenticate.encode_env()); // Authentication headers l.push_back(hdr_auth_info.encode_env()); l.push_back(hdr_authorization.encode_env()); l.push_back(hdr_www_authenticate.encode_env()); // Remaining headers in alphabetical order l.push_back(hdr_accept.encode_env()); l.push_back(hdr_accept_encoding.encode_env()); l.push_back(hdr_accept_language.encode_env()); l.push_back(hdr_alert_info.encode_env()); l.push_back(hdr_allow.encode_env()); l.push_back(hdr_allow_events.encode_env()); l.push_back(hdr_call_info.encode_env()); l.push_back(hdr_content_disp.encode_env()); l.push_back(hdr_content_encoding.encode_env()); l.push_back(hdr_content_language.encode_env()); l.push_back(hdr_date.encode_env()); l.push_back(hdr_error_info.encode_env()); l.push_back(hdr_event.encode_env()); l.push_back(hdr_expires.encode_env()); l.push_back(hdr_in_reply_to.encode_env()); l.push_back(hdr_min_expires.encode_env()); l.push_back(hdr_mime_version.encode_env()); l.push_back(hdr_organization.encode_env()); l.push_back(hdr_priority.encode_env()); l.push_back(hdr_rack.encode_env()); l.push_back(hdr_refer_sub.encode_env()); l.push_back(hdr_refer_to.encode_env()); l.push_back(hdr_referred_by.encode_env()); l.push_back(hdr_replaces.encode_env()); l.push_back(hdr_reply_to.encode_env()); l.push_back(hdr_require.encode_env()); l.push_back(hdr_request_disposition.encode_env()); l.push_back(hdr_retry_after.encode_env()); l.push_back(hdr_rseq.encode_env()); l.push_back(hdr_server.encode_env()); l.push_back(hdr_sip_etag.encode_env()); l.push_back(hdr_sip_if_match.encode_env()); l.push_back(hdr_subject.encode_env()); l.push_back(hdr_subscription_state.encode_env()); l.push_back(hdr_supported.encode_env()); l.push_back(hdr_timestamp.encode_env()); l.push_back(hdr_unsupported.encode_env()); l.push_back(hdr_user_agent.encode_env()); l.push_back(hdr_warning.encode_env()); // Unknown headers for (list::const_iterator i = unknown_headers.begin(); i != unknown_headers.end(); i++) { string s = "SIP_"; s += toupper(replace_char(i->name, '-', '_')); s += '='; s += i->value; l.push_back(s); } l.push_back(hdr_content_length.encode_env()); return l; } t_sip_message *t_sip_message::copy(void) const { t_sip_message *m = new t_sip_message(*this); MEMMAN_NEW(m); return m; } void t_sip_message::set_body_plain_text(const string &text, const string &charset) { // Content-Type header t_media mime_type("text", "plain"); mime_type.charset = charset; hdr_content_type.set_media(mime_type); if (body) { MEMMAN_DELETE(body); delete body; } body = new t_sip_body_plain_text(text); MEMMAN_NEW(body); } bool t_sip_message::set_body_from_file(const string &filename, const t_media &media) { // Open file and set read pointer at end so we know the size. ifstream f(filename.c_str(), ios::binary); if (!f) return false; ostringstream body_stream(ios::binary); // Copy file into body body_stream << f.rdbuf(); if (!f.good() || !body_stream.good()) { return false; } // Create body of correct type t_sip_body *new_body = NULL; if (media.type == "text" && media.subtype == "plain") { t_sip_body_plain_text *text_body = new t_sip_body_plain_text(body_stream.str()); MEMMAN_NEW(text_body); new_body = text_body; } else { t_sip_body_opaque *opaque_body = new t_sip_body_opaque(body_stream.str()); MEMMAN_NEW(opaque_body); new_body = opaque_body; } if (body) { MEMMAN_DELETE(body); delete body; } body = new_body; // Content-Type header hdr_content_type.set_media(media); return true; } size_t t_sip_message::get_encoded_size(void) { string s = encode(); return s.size(); } bool t_sip_message::local_ip_check(void) const { if (get_type() == MSG_REQUEST && hdr_via.is_populated()) { const t_via &v = hdr_via.via_list.front(); if (v.host == "0.0.0.0") return false; } if (hdr_contact.is_populated()) { if (!hdr_contact.any_flag && !hdr_contact.contact_list.empty()) { const t_contact_param &c = hdr_contact.contact_list.front(); if (c.uri.get_host() == "0.0.0.0") return false; } } if (body) { return body->local_ip_check(); } return true; } void t_sip_message::calc_local_ip(void) { // Do nothing } unsigned long t_sip_message::get_local_ip(void) { if (local_ip_ == 0) calc_local_ip(); return local_ip_; }