/*! * \internal * \brief Copies any other request header data over to ast_msg structure. * * \param rdata The SIP request * \param msg The msg structure to copy headers into */ static int headers_to_vars(const pjsip_rx_data *rdata, struct ast_msg *msg) { char *c; char name[MAX_HDR_SIZE]; char buf[MAX_HDR_SIZE]; int res = 0; pjsip_hdr *h = rdata->msg_info.msg->hdr.next; pjsip_hdr *end= &rdata->msg_info.msg->hdr; while (h != end) { if ((res = pjsip_hdr_print_on(h, buf, sizeof(buf)-1)) > 0) { buf[res] = '\0'; if ((c = strchr(buf, ':'))) { ast_copy_string(buf, ast_skip_blanks(c + 1), sizeof(buf)); } ast_copy_pj_str(name, &h->name, sizeof(name)); if ((res = ast_msg_set_var(msg, name, buf)) != 0) { break; } } h = h->next; } return 0; }
void SIPCall::hangup(int reason) { // Stop all RTP streams stopAllMedia(); if (not inv or not inv->dlg) { removeCall(); throw VoipLinkException("No invite session for this call"); } pjsip_route_hdr *route = inv->dlg->route_set.next; while (route and route != &inv->dlg->route_set) { char buf[1024]; int printed = pjsip_hdr_print_on(route, buf, sizeof(buf)); if (printed >= 0) { buf[printed] = '\0'; RING_DBG("[call:%s] Route header %s", getCallId().c_str(), buf); } route = route->next; } const int status = reason ? reason : inv->state <= PJSIP_INV_STATE_EARLY and inv->role != PJSIP_ROLE_UAC ? PJSIP_SC_CALL_TSX_DOES_NOT_EXIST : inv->state >= PJSIP_INV_STATE_DISCONNECTED ? PJSIP_SC_DECLINE : 0; // Notify the peer terminateSipSession(status); setState(Call::ConnectionState::DISCONNECTED, reason); removeCall(); }
/*! * \internal * \brief Implements PJSIP_HEADER 'read' by searching the for the requested header. * * Retrieve the header_datastore. * Search for the nth matching header. * Validate the pjsip_hdr found. * Parse pjsip_hdr into a name and value. * Return the value. */ static int read_header(void *obj) { struct header_data *data = obj; pjsip_hdr *hdr = NULL; char *pj_hdr_string; size_t pj_hdr_string_len; char *p; size_t plen; RAII_VAR(struct ast_datastore *, datastore, ast_sip_session_get_datastore(data->channel->session, header_datastore.type), ao2_cleanup); if (!datastore || !datastore->data) { ast_debug(1, "There was no datastore from which to read headers.\n"); return -1; } hdr = find_header((struct hdr_list *) datastore->data, data->header_name, data->header_number); if (!hdr) { ast_debug(1, "There was no header named %s.\n", data->header_name); return -1; } pj_hdr_string = ast_alloca(data->len); pj_hdr_string_len = pjsip_hdr_print_on(hdr, pj_hdr_string, data->len); pj_hdr_string[pj_hdr_string_len] = '\0'; p = strchr(pj_hdr_string, ':'); if (!p) { ast_log(AST_LOG_ERROR, "A malformed header was returned from pjsip_hdr_print_on.\n"); return -1; } ++p; p = ast_strip(p); plen = strlen(p); if (plen + 1 > data->len) { ast_log(AST_LOG_ERROR, "Buffer isn't big enough to hold header value. %zu > %zu\n", plen + 1, data->len); return -1; } ast_copy_string(data->buf, p, data->len); return 0; }
std::string PJUtils::get_header_value(pjsip_hdr* header) { #define MAX_HDR_SIZE 4096 char buf[MAX_HDR_SIZE] = ""; char* buf2 = buf; int len = pjsip_hdr_print_on(header, buf2, MAX_HDR_SIZE); // pjsip_hdr_print_on doesn't appear to null-terminate the string - do this by hand buf2[len] = '\0'; // Skip over all text up to the colon, then any whitespace following it while ((*buf2 != ':') || (*buf2 == ' ')) { buf2++; } return std::string(buf2, len); }
// Reject request unless it's a SUBSCRIBE targeted at the home domain / this node. pj_bool_t subscription_on_rx_request(pjsip_rx_data *rdata) { SAS::TrailId trail = get_trail(rdata); if (rdata->tp_info.transport->local_name.port != stack_data.scscf_port) { // Not an S-CSCF, so don't handle SUBSCRIBEs. return PJ_FALSE; // LCOV_EXCL_LINE } if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method())) { // This isn't a SUBSCRIBE, so this module can't process it. return PJ_FALSE; } if (!((PJUtils::is_home_domain(rdata->msg_info.msg->line.req.uri) || (PJUtils::is_uri_local(rdata->msg_info.msg->line.req.uri))) && PJUtils::check_route_headers(rdata))) { LOG_DEBUG("Rejecting subscription request not targeted at this domain or node"); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_DOMAIN, 0); SAS::report_event(event); return PJ_FALSE; } // SUBSCRIBE request targeted at the home domain or specifically at this node. Check // whether it should be processed by this module or passed up to an AS. pjsip_msg *msg = rdata->msg_info.msg; // A valid subscription must have the Event header set to "reg". This is case-sensitive pj_str_t event_name = pj_str("Event"); pjsip_event_hdr* event = (pjsip_event_hdr*)pjsip_msg_find_hdr_by_name(msg, &event_name, NULL); if (!event || (PJUtils::pj_str_to_string(&event->event_type) != "reg")) { // The Event header is missing or doesn't match "reg" LOG_DEBUG("Rejecting subscription request with invalid event header"); SAS::Event sas_event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_EVENT, 0); if (event) { char event_hdr_str[256]; memset(event_hdr_str, 0, 256); pjsip_hdr_print_on(event, event_hdr_str, 255); sas_event.add_var_param(event_hdr_str); } SAS::report_event(sas_event); return PJ_FALSE; } // Accept header may be present - if so must include the application/reginfo+xml pjsip_accept_hdr* accept = (pjsip_accept_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_ACCEPT, NULL); if (accept) { bool found = false; pj_str_t reginfo = pj_str("application/reginfo+xml"); for (uint32_t i = 0; i < accept->count; i++) { if (!pj_strcmp(accept->values + i, ®info)) { found = true; } } if (!found) { // The Accept header (if it exists) doesn't contain "application/reginfo+xml" LOG_DEBUG("Rejecting subscription request with invalid accept header"); char accept_hdr_str[256]; memset(accept_hdr_str, 0, 256); pjsip_hdr_print_on(accept, accept_hdr_str, 255); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_ACCEPT, 0); event.add_var_param(accept_hdr_str); SAS::report_event(event); return PJ_FALSE; } } process_subscription_request(rdata); return PJ_TRUE; }
static int multipart_print_body(struct pjsip_msg_body *msg_body, char *buf, pj_size_t size) { const struct multipart_data *m_data; pj_str_t clen_hdr = { "Content-Length: ", 16}; pjsip_multipart_part *part; char *p = buf, *end = buf+size; #define SIZE_LEFT() (end-p) m_data = (const struct multipart_data*)msg_body->data; PJ_ASSERT_RETURN(m_data && !pj_list_empty(&m_data->part_head), PJ_EINVAL); part = m_data->part_head.next; while (part != &m_data->part_head) { enum { CLEN_SPACE = 5 }; char *clen_pos; const pjsip_hdr *hdr; clen_pos = NULL; /* Print delimiter */ if (SIZE_LEFT() <= (m_data->boundary.slen+8) << 1) return -1; *p++ = 13; *p++ = 10; *p++ = '-'; *p++ = '-'; pj_memcpy(p, m_data->boundary.ptr, m_data->boundary.slen); p += m_data->boundary.slen; *p++ = 13; *p++ = 10; /* Print optional headers */ hdr = part->hdr.next; while (hdr != &part->hdr) { int printed = pjsip_hdr_print_on((pjsip_hdr*)hdr, p, SIZE_LEFT()-2); if (printed < 0) return -1; p += printed; *p++ = '\r'; *p++ = '\n'; hdr = hdr->next; } /* Automaticly adds Content-Type and Content-Length headers, only * if content_type is set in the message body. */ if (part->body && part->body->content_type.type.slen) { pj_str_t ctype_hdr = { "Content-Type: ", 14}; const pjsip_media_type *media = &part->body->content_type; if (pjsip_use_compact_form) { ctype_hdr.ptr = "c: "; ctype_hdr.slen = 3; } /* Add Content-Type header. */ if ( (end-p) < 24 + media->type.slen + media->subtype.slen) { return -1; } pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen); p += ctype_hdr.slen; p += pjsip_media_type_print(p, (unsigned)(end-p), media); *p++ = '\r'; *p++ = '\n'; /* Add Content-Length header. */ if ((end-p) < clen_hdr.slen + 12 + 2) { return -1; } pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); p += clen_hdr.slen; /* Print blanks after "Content-Length:", this is where we'll put * the content length value after we know the length of the * body. */ pj_memset(p, ' ', CLEN_SPACE); clen_pos = p; p += CLEN_SPACE; *p++ = '\r'; *p++ = '\n'; } /* Empty newline */ *p++ = 13; *p++ = 10; /* Print the body */ pj_assert(part->body != NULL); if (part->body) { int printed = part->body->print_body(part->body, p, SIZE_LEFT()); if (printed < 0) return -1; p += printed; /* Now that we have the length of the body, print this to the * Content-Length header. */ if (clen_pos) { char tmp[16]; int len; len = pj_utoa(printed, tmp); if (len > CLEN_SPACE) len = CLEN_SPACE; pj_memcpy(clen_pos+CLEN_SPACE-len, tmp, len); } } part = part->next; } /* Print closing delimiter */ if (SIZE_LEFT() < m_data->boundary.slen+8) return -1; *p++ = 13; *p++ = 10; *p++ = '-'; *p++ = '-'; pj_memcpy(p, m_data->boundary.ptr, m_data->boundary.slen); p += m_data->boundary.slen; *p++ = '-'; *p++ = '-'; *p++ = 13; *p++ = 10; #undef SIZE_LEFT return (int)(p - buf); }
// Check whether this request should be absorbed by the subscription module bool SubscriptionSproutlet::handle_request(pjsip_msg* req, SAS::TrailId trail) { if (pjsip_method_cmp(&req->line.req.method, pjsip_get_subscribe_method())) { // This isn't a SUBSCRIBE, so this module can't process it. return false; } URIClass uri_class = URIClassifier::classify_uri(req->line.req.uri); if (((uri_class != NODE_LOCAL_SIP_URI) && (uri_class != HOME_DOMAIN_SIP_URI) && (uri_class != GLOBAL_PHONE_NUMBER) && (uri_class != LOCAL_PHONE_NUMBER)) || !PJUtils::check_route_headers(req)) { TRC_DEBUG("Not processing subscription request not targeted at this domain " "or node"); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_DOMAIN, 0); SAS::report_event(event); return false; } // We now know we have a SUBSCRIBE request targeted at the home domain // or specifically at this node. Check whether it should be processed // by this module or passed up to an AS. // A valid subscription must have the Event header set to "reg". This is // case-sensitive. pj_str_t event_name = pj_str((char*)"Event"); pjsip_event_hdr* event = (pjsip_event_hdr*)pjsip_msg_find_hdr_by_name(req, &event_name, NULL); if (!event || (PJUtils::pj_str_to_string(&event->event_type) != "reg")) { // The Event header is missing or doesn't match "reg" TRC_DEBUG("Not processing subscribe that's not for the 'reg' package"); SAS::Event sas_event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_EVENT, 0); if (event) { char event_hdr_str[256]; memset(event_hdr_str, 0, 256); pjsip_hdr_print_on(event, event_hdr_str, 255); sas_event.add_var_param(event_hdr_str); } SAS::report_event(sas_event); return false; } // Accept header may be present - if so must include application/reginfo+xml pjsip_accept_hdr* accept = (pjsip_accept_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_ACCEPT, NULL); if (accept) { bool found = false; pj_str_t reginfo = pj_str((char*)"application/reginfo+xml"); for (uint32_t i = 0; i < accept->count; i++) { if (!pj_strcmp(accept->values + i, ®info)) { found = true; break; } } if (!found) { // The Accept header (if it exists) doesn't contain // "application/reginfo+xml" TRC_DEBUG("Not processing subscription request that doesn't " "accept reginfo notifications"); char accept_hdr_str[256]; memset(accept_hdr_str, 0, 256); pjsip_hdr_print_on(accept, accept_hdr_str, 255); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_ACCEPT, 0); event.add_var_param(accept_hdr_str); SAS::report_event(event); return false; } } return true; }
static void print_call(int call_index) { struct call *call = &app.call[call_index]; int len; pjsip_inv_session *inv = call->inv; pjsip_dialog *dlg = inv->dlg; struct media_stream *audio = &call->media[0]; char userinfo[128]; char duration[80], last_update[80]; char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16]; unsigned decor; pj_time_val now; decor = pj_log_get_decor(); pj_log_set_decor(PJ_LOG_HAS_NEWLINE); pj_gettimeofday(&now); if (app.report_filename) puts(app.report_filename); /* Print duration */ if (inv->state >= PJSIP_INV_STATE_CONFIRMED && call->connect_time.sec) { PJ_TIME_VAL_SUB(now, call->connect_time); sprintf(duration, " [duration: %02ld:%02ld:%02ld.%03ld]", now.sec / 3600, (now.sec % 3600) / 60, (now.sec % 60), now.msec); } else { duration[0] = '\0'; } /* Call number and state */ PJ_LOG(3, (THIS_FILE, "Call #%d: %s%s", call_index, pjsip_inv_state_name(inv->state), duration)); /* Call identification */ len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo)); if (len < 0) pj_ansi_strcpy(userinfo, "<--uri too long-->"); else userinfo[len] = '\0'; PJ_LOG(3, (THIS_FILE, " %s", userinfo)); if (call->inv == NULL || call->inv->state < PJSIP_INV_STATE_CONFIRMED || call->connect_time.sec == 0) { pj_log_set_decor(decor); return; } /* Signaling quality */ { char pdd[64], connectdelay[64]; pj_time_val t; if (call->response_time.sec) { t = call->response_time; PJ_TIME_VAL_SUB(t, call->start_time); sprintf(pdd, "got 1st response in %ld ms", PJ_TIME_VAL_MSEC(t)); } else { pdd[0] = '\0'; } if (call->connect_time.sec) { t = call->connect_time; PJ_TIME_VAL_SUB(t, call->start_time); sprintf(connectdelay, ", connected after: %ld ms", PJ_TIME_VAL_MSEC(t)); } else { connectdelay[0] = '\0'; } PJ_LOG(3, (THIS_FILE, " Signaling quality: %s%s", pdd, connectdelay)); } PJ_LOG(3, (THIS_FILE, " Stream #0: audio %.*s@%dHz, %dms/frame, %sB/s (%sB/s +IP hdr)", (int)audio->si.fmt.encoding_name.slen, audio->si.fmt.encoding_name.ptr, audio->clock_rate, audio->samples_per_frame * 1000 / audio->clock_rate, good_number(bps, audio->bytes_per_frame * audio->clock_rate / audio->samples_per_frame), good_number(ipbps, (audio->bytes_per_frame+32) * audio->clock_rate / audio->samples_per_frame))); if (audio->rtcp.stat.rx.update_cnt == 0) strcpy(last_update, "never"); else { pj_gettimeofday(&now); PJ_TIME_VAL_SUB(now, audio->rtcp.stat.rx.update); sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", now.sec / 3600, (now.sec % 3600) / 60, now.sec % 60, now.msec); } PJ_LOG(3, (THIS_FILE, " RX stat last update: %s\n" " total %s packets %sB received (%sB +IP hdr)%s\n" " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n" " (msec) min avg max last\n" " loss period: %7.3f %7.3f %7.3f %7.3f%s\n" " jitter : %7.3f %7.3f %7.3f %7.3f%s", last_update, good_number(packets, audio->rtcp.stat.rx.pkt), good_number(bytes, audio->rtcp.stat.rx.bytes), good_number(ipbytes, audio->rtcp.stat.rx.bytes + audio->rtcp.stat.rx.pkt * 32), "", audio->rtcp.stat.rx.loss, audio->rtcp.stat.rx.loss * 100.0 / (audio->rtcp.stat.rx.pkt + audio->rtcp.stat.rx.loss), audio->rtcp.stat.rx.dup, audio->rtcp.stat.rx.dup * 100.0 / (audio->rtcp.stat.rx.pkt + audio->rtcp.stat.rx.loss), audio->rtcp.stat.rx.reorder, audio->rtcp.stat.rx.reorder * 100.0 / (audio->rtcp.stat.rx.pkt + audio->rtcp.stat.rx.loss), "", audio->rtcp.stat.rx.loss_period.min / 1000.0, audio->rtcp.stat.rx.loss_period.mean / 1000.0, audio->rtcp.stat.rx.loss_period.max / 1000.0, audio->rtcp.stat.rx.loss_period.last / 1000.0, "", audio->rtcp.stat.rx.jitter.min / 1000.0, audio->rtcp.stat.rx.jitter.mean / 1000.0, audio->rtcp.stat.rx.jitter.max / 1000.0, audio->rtcp.stat.rx.jitter.last / 1000.0, "" )); if (audio->rtcp.stat.tx.update_cnt == 0) strcpy(last_update, "never"); else { pj_gettimeofday(&now); PJ_TIME_VAL_SUB(now, audio->rtcp.stat.tx.update); sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", now.sec / 3600, (now.sec % 3600) / 60, now.sec % 60, now.msec); } PJ_LOG(3, (THIS_FILE, " TX stat last update: %s\n" " total %s packets %sB sent (%sB +IP hdr)%s\n" " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n" " (msec) min avg max last\n" " loss period: %7.3f %7.3f %7.3f %7.3f%s\n" " jitter : %7.3f %7.3f %7.3f %7.3f%s", last_update, good_number(packets, audio->rtcp.stat.tx.pkt), good_number(bytes, audio->rtcp.stat.tx.bytes), good_number(ipbytes, audio->rtcp.stat.tx.bytes + audio->rtcp.stat.tx.pkt * 32), "", audio->rtcp.stat.tx.loss, audio->rtcp.stat.tx.loss * 100.0 / (audio->rtcp.stat.tx.pkt + audio->rtcp.stat.tx.loss), audio->rtcp.stat.tx.dup, audio->rtcp.stat.tx.dup * 100.0 / (audio->rtcp.stat.tx.pkt + audio->rtcp.stat.tx.loss), audio->rtcp.stat.tx.reorder, audio->rtcp.stat.tx.reorder * 100.0 / (audio->rtcp.stat.tx.pkt + audio->rtcp.stat.tx.loss), "", audio->rtcp.stat.tx.loss_period.min / 1000.0, audio->rtcp.stat.tx.loss_period.mean / 1000.0, audio->rtcp.stat.tx.loss_period.max / 1000.0, audio->rtcp.stat.tx.loss_period.last / 1000.0, "", audio->rtcp.stat.tx.jitter.min / 1000.0, audio->rtcp.stat.tx.jitter.mean / 1000.0, audio->rtcp.stat.tx.jitter.max / 1000.0, audio->rtcp.stat.tx.jitter.last / 1000.0, "" )); PJ_LOG(3, (THIS_FILE, " RTT delay : %7.3f %7.3f %7.3f %7.3f%s\n", audio->rtcp.stat.rtt.min / 1000.0, audio->rtcp.stat.rtt.mean / 1000.0, audio->rtcp.stat.rtt.max / 1000.0, audio->rtcp.stat.rtt.last / 1000.0, "" )); pj_log_set_decor(decor); }