FreeCalypso > hg > themwi-system-sw
view sip-in/invite.c @ 109:9b87894704eb
sip-in: first step toward final call clearing
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Wed, 28 Sep 2022 16:32:13 -0800 |
parents | 0d6435808bcd |
children | c1c94b7fc2e2 |
line wrap: on
line source
/* * Here we implement our handling of SIP INVITE method. */ #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <syslog.h> #include "../include/gsm48_const.h" #include "../libsip/parse.h" #include "../libsip/uas_basic.h" #include "../libsip/grok_from.h" #include "../libsip/req_supp.h" #include "../libsip/sdp.h" #include "../libsip/out_msg.h" #include "call.h" extern struct in_addr sip_bind_ip; extern unsigned sip_bind_port; extern int cfg_use_100rel; extern int cfg_force_pcma; extern struct call *call_list; extern unsigned sip_linger_error; extern struct call *find_call_by_sip_id(); extern char *get_single_header(); fill_invite_resp_from_call(msg, call) struct sip_msg_out *msg; struct call *call; { char cseq_str[32]; int rc; rc = out_msg_add_header(msg, "From", call->invite_from); if (rc < 0) return rc; rc = out_msg_add_header(msg, "To", call->invite_to); if (rc < 0) return rc; rc = out_msg_add_header(msg, "Call-ID", call->sip_call_id); if (rc < 0) return rc; sprintf(cseq_str, "%u INVITE", call->invite_cseq); rc = out_msg_add_header(msg, "CSeq", cseq_str); if (rc < 0) return rc; return out_msg_add_header(msg, "Via", call->invite_via); } fill_invite_200_resp(msg, call) struct sip_msg_out *msg; struct call *call; { char contact_str[80]; struct sdp_gen sdp; int rc; start_response_out_msg(msg, "200 CONNECT"); rc = fill_invite_resp_from_call(msg, call); if (rc < 0) return rc; sprintf(contact_str, "<sip:%s:%u;transport=udp>", inet_ntoa(sip_bind_ip), sip_bind_port); rc = out_msg_add_header(msg, "Contact", contact_str); if (rc < 0) return rc; rc = out_msg_add_header(msg, "Content-Type", "application/sdp"); if (rc < 0) return rc; bzero(&sdp, sizeof sdp); sdp.conn_ip = call->pstn_rtp_local.sin_addr; sdp.conn_port = ntohs(call->pstn_rtp_local.sin_port); sdp.codec_mask = call->use_pcma ? SDP_CODEC_MASK_PCMA : SDP_CODEC_MASK_PCMU; sdp.session_id = (sdp.conn_port << 16) | call->sdp_addend; sdp.owner_ip = sip_bind_ip; return out_msg_finish_sdp(msg, &sdp); } static void invite_new_call(req, ess, sin) struct sip_pkt_rx *req; struct uas_parse_hdrs *ess; struct sockaddr_in *sin; { static unsigned cycle_tag_num, cycle_sdp_addend; char uri_user[13], *called_nanp; struct sip_msg_out resp; struct grok_from gfrom; struct supported_ext supp_ext; char *hval, *unsup_ext; int ext_100rel_req, ext_100rel_sup, use_100rel, use_pcma; struct sdp_parse sdp_parse; struct sdp_gen sdp_gen; struct call *call; char *dp; unsigned req_uri_len, to_hdr_len, copylen; int rc; /* extract called number from Request-URI */ rc = user_from_sip_uri(req->req_uri, uri_user, 12); if (rc < 0) { not_nanp: start_response_out_msg(&resp, "416 Request-URI is not a NANP number"); error_resp: rc = add_resp_basic_headers(&resp, ess, req->req_method); if (rc < 0) { error_resp_toolong: syslog(LOG_ERR, "INVITE early error response length exceeded"); return; } out_msg_finish(&resp); sip_tx_packet(&resp, sin); return; } if (uri_user[0] == '+') { if (grok_number_string(uri_user+1, 0) != 11 || uri_user[1] != '1') goto not_nanp; called_nanp = uri_user + 2; } else switch (grok_number_string(uri_user)) { case 10: called_nanp = uri_user; break; case 11: if (uri_user[0] != '1') goto not_nanp; called_nanp = uri_user + 1; break; default: goto not_nanp; } if (!is_nanp_valid_prefix(called_nanp)) goto not_nanp; /* it is valid NANP - but is it one of ours? */ refresh_number_db(); if (!is_nanp_locally_owned(called_nanp)) { start_response_out_msg(&resp, "404 Called number does not belong here"); goto error_resp; } /* parse and validate From header */ rc = grok_from_header(ess->from, &gfrom); if (rc < 0) { start_response_out_msg(&resp, "400 Malformed From header"); goto error_resp; } /* validate To header for the purpose of tag addition */ req_uri_len = strlen(req->req_uri); to_hdr_len = strlen(ess->to); if (to_hdr_len == req_uri_len) { if (strcasecmp(ess->to, req->req_uri)) { bad_to_header: start_response_out_msg(&resp, "400 Bad To header"); goto error_resp; } } else if (to_hdr_len == req_uri_len + 2) { if (ess->to[0] != '<') goto bad_to_header; if (strncasecmp(ess->to+1, req->req_uri, req_uri_len)) goto bad_to_header; if (ess->to[req_uri_len+1] != '>') goto bad_to_header; } else goto bad_to_header; /* check 100rel and catch any unsupported requirements */ supp_ext.name = "100rel"; supp_ext.req_flag = &ext_100rel_req; supp_ext.sup_flag = &ext_100rel_sup; ext_100rel_req = ext_100rel_sup = 0; rc = parse_require_supported(req, &supp_ext, 1, &unsup_ext); if (rc < 0) { start_response_out_msg(&resp, "420 Extension not supported"); rc = out_msg_add_header(&resp, "Unsupported", unsup_ext); if (rc < 0) goto error_resp_toolong; goto error_resp; } if (ext_100rel_req) use_100rel = 1; else if (ext_100rel_sup) use_100rel = cfg_use_100rel; else use_100rel = 0; /* did the caller send an SDP message body? */ if (!req->msg_body_len) { start_response_out_msg(&resp, "415 Missing SDP body"); error_415: rc = out_msg_add_header(&resp, "Accept", "application/sdp"); if (rc < 0) goto error_resp_toolong; goto error_resp; } hval = get_single_header(req, "Content-Type", "c", (int *) 0); if (!hval) { start_response_out_msg(&resp, "415 Missing Content-Type header"); goto error_415; } if (strcasecmp(hval, "application/sdp")) { start_response_out_msg(&resp, "415 Unsupported Content-Type"); goto error_415; } rc = parse_incoming_sdp(req->msg_body, req->msg_body_len, &sdp_parse); if (rc < 0) { start_response_out_msg(&resp, "488 Malformed SDP body"); goto error_resp; } switch (sdp_parse.codec_mask) { case SDP_CODEC_MASK_PCMU: use_pcma = 0; break; case SDP_CODEC_MASK_PCMA: case SDP_CODEC_MASK_BOTH | SDP_CODEC_MASK_PCMA_PREF: use_pcma = 1; break; case SDP_CODEC_MASK_BOTH: use_pcma = cfg_force_pcma; break; default: start_response_out_msg(&resp, "488 Unsupported codec selection"); rc = add_resp_basic_headers(&resp, ess, req->req_method); if (rc < 0) goto error_resp_toolong; rc = out_msg_add_header(&resp, "Content-Type", "application/sdp"); if (rc < 0) goto error_resp_toolong; bzero(&sdp_gen, sizeof sdp_gen); sdp_gen.owner_ip = sip_bind_ip; sdp_gen.conn_ip = sip_bind_ip; sdp_gen.codec_mask = SDP_CODEC_MASK_BOTH; rc = out_msg_finish_sdp(&resp, &sdp_gen); if (rc < 0) goto error_resp_toolong; sip_tx_packet(&resp, sin); return; } /* SIP INVITE validation done - check if GSM service is up */ rc = connect_gsm_mtcall(); if (rc < 0) { gsm_offline: start_response_out_msg(&resp, "480 GSM service is offline"); goto error_resp; } rc = connect_tmgw_socket(); if (rc < 0) goto gsm_offline; /* stateful processing begins */ call = malloc(sizeof(struct call) + strlen(ess->call_id) + strlen(ess->from) + req_uri_len + strlen(ess->via) + 19); if (!call) { syslog(LOG_CRIT, "failed malloc for incoming call!"); start_response_out_msg(&resp, "503 Gateway resource allocation failure"); goto error_resp; } cycle_tag_num++; if (cycle_tag_num >= 1000000) cycle_tag_num = 0; cycle_sdp_addend++; if (cycle_sdp_addend >= 0x10000) cycle_sdp_addend = 0; bzero(call, sizeof(struct call)); dp = (char *)(call + 1); copylen = strlen(ess->call_id) + 1; call->sip_call_id = dp; bcopy(ess->call_id, dp, copylen); dp += copylen; copylen = strlen(ess->from) + 1; call->invite_from = dp; bcopy(ess->from, dp, copylen); dp += copylen; call->invite_to = dp; *dp++ = '<'; bcopy(req->req_uri, dp, req_uri_len); dp += req_uri_len; *dp++ = '>'; sprintf(dp, ";tag=in%06u", cycle_tag_num); dp += 14; copylen = strlen(ess->via) + 1; call->invite_via = dp; bcopy(ess->via, dp, copylen); call->invite_cseq = ess->cseq_num; bcopy(sin, &call->udp_sin, sizeof(struct sockaddr_in)); bcopy(called_nanp, call->called_nanp, 11); call->from_uri = call->invite_from + (gfrom.uri - ess->from); call->from_uri_len = gfrom.uri_len; call->from_user = call->invite_from + (gfrom.user - ess->from); call->from_user_len = gfrom.user_len; call->use_100rel = use_100rel; call->pstn_rtp_remote.sin_family = AF_INET; call->pstn_rtp_remote.sin_addr = sdp_parse.ip_addr; call->pstn_rtp_remote.sin_port = htons(sdp_parse.audio_port); call->use_pcma = use_pcma; call->sdp_addend = cycle_sdp_addend; /* generate 100 response */ start_response_out_msg(&resp, "100 Proceeding"); rc = fill_invite_resp_from_call(&resp, call); if (rc < 0) { syslog(LOG_ERR, "INVITE 100 response length exceeded"); free(call); return; } out_msg_finish(&resp); sip_tx_packet(&resp, sin); /* add to call list */ call->next = call_list; call_list = call; /* send CRCX to TMGW */ tmgw_send_crcx(call); call->overall_state = OVERALL_STATE_CRCX; call->sip_state = SIP_STATE_INVITE_PROC; } static void invite_existing_call(req, ess, sin, call) struct sip_pkt_rx *req; struct uas_parse_hdrs *ess; struct sockaddr_in *sin; struct call *call; { struct sip_msg_out resp; int rc; if (ess->cseq_num != call->invite_cseq) { start_response_out_msg(&resp, "501 Re-INVITE not supported"); rc = add_resp_basic_headers(&resp, ess, req->req_method); if (rc < 0) { syslog(LOG_ERR, "sending 501 Re-INVITE error: response length exceeded"); return; } out_msg_finish(&resp); sip_tx_packet(&resp, sin); return; } /* it's a retransmission, not a re-INVITE */ switch (call->sip_state) { case SIP_STATE_INVITE_PROC: start_response_out_msg(&resp, "100 Proceeding"); fill_invite_resp_from_call(&resp, call); out_msg_finish(&resp); sip_tx_packet(&resp, sin); return; case SIP_STATE_RINGING: start_response_out_msg(&resp, "180 Ringing"); fill_invite_resp_from_call(&resp, call); out_msg_finish(&resp); sip_tx_packet(&resp, sin); return; case SIP_STATE_RINGING_REL: start_response_out_msg(&resp, "180 Ringing"); fill_invite_resp_from_call(&resp, call); out_msg_add_header(&resp, "Require", "100rel"); out_msg_add_header(&resp, "RSeq", "1"); out_msg_finish(&resp); sip_tx_packet(&resp, sin); return; case SIP_STATE_INVITE_200: case SIP_STATE_CONNECTED: fill_invite_200_resp(&resp, call); sip_tx_packet(&resp, sin); return; case SIP_STATE_INVITE_ERR: start_response_out_msg(&resp, call->invite_fail); fill_invite_resp_from_call(&resp, call); out_msg_finish(&resp); sip_tx_packet(&resp, sin); return; default: /* silently discard */ return; } } void handle_sip_invite(req, ess, sin) struct sip_pkt_rx *req; struct uas_parse_hdrs *ess; struct sockaddr_in *sin; { struct call *call; call = find_call_by_sip_id(ess->call_id); if (call) invite_existing_call(req, ess, sin, call); else invite_new_call(req, ess, sin); } void signal_invite_ringing(call) struct call *call; { struct sip_msg_out resp; int rc; start_response_out_msg(&resp, "180 Ringing"); rc = fill_invite_resp_from_call(&resp, call); if (rc < 0) { msg_size_err: syslog(LOG_ERR, "INVITE 180 response length exceeded"); call->sip_state = SIP_STATE_MSG_SIZE_ERR; call->overall_state = OVERALL_STATE_TEARDOWN; disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_INTERWORKING); disconnect_tmgw(call); sip_mark_end_time(call, sip_linger_error); /* TODO: transition from TEARDOWN to DEAD_SIP */ return; } if (call->use_100rel) { rc = out_msg_add_header(&resp, "Require", "100rel"); if (rc < 0) goto msg_size_err; rc = out_msg_add_header(&resp, "RSeq", "1"); if (rc < 0) goto msg_size_err; } out_msg_finish(&resp); sip_tx_packet(&resp, &call->udp_sin); if (call->use_100rel) { call->sip_state = SIP_STATE_RINGING_REL; call->sip_tx_count = 1; } else call->sip_state = SIP_STATE_RINGING; } void signal_invite_200(call) struct call *call; { struct sip_msg_out resp; int rc; rc = fill_invite_200_resp(&resp, call); if (rc < 0) { syslog(LOG_ERR, "INVITE 200 response length exceeded"); call->sip_state = SIP_STATE_MSG_SIZE_ERR; call->overall_state = OVERALL_STATE_TEARDOWN; disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_INTERWORKING); disconnect_tmgw(call); sip_mark_end_time(call, sip_linger_error); /* TODO: transition from TEARDOWN to DEAD_SIP */ return; } sip_tx_packet(&resp, &call->udp_sin); call->sip_state = SIP_STATE_INVITE_200; call->sip_tx_count = 1; } void signal_invite_error(call) struct call *call; { struct sip_msg_out resp; int rc; start_response_out_msg(&resp, call->invite_fail); rc = fill_invite_resp_from_call(&resp, call); if (rc < 0) { syslog(LOG_ERR, "INVITE late error response length exceeded"); call->sip_state = SIP_STATE_MSG_SIZE_ERR; sip_mark_end_time(call, sip_linger_error); /* TODO: transition from TEARDOWN to DEAD_SIP */ return; } out_msg_finish(&resp); sip_tx_packet(&resp, &call->udp_sin); call->sip_state = SIP_STATE_INVITE_ERR; call->sip_tx_count = 1; }