FreeCalypso > hg > themwi-system-sw
diff sip-in/invite.c @ 60:02761f1ae5e5
sip-in INVITE processing: got as far as CRCX completion
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sun, 11 Sep 2022 15:42:54 -0800 |
parents | d61d0136f6a5 |
children | 7c0309df59f8 |
line wrap: on
line diff
--- a/sip-in/invite.c Thu Sep 08 14:53:18 2022 -0800 +++ b/sip-in/invite.c Sun Sep 11 15:42:54 2022 -0800 @@ -5,26 +5,84 @@ #include <sys/types.h> #include <sys/socket.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 <unistd.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 struct call *call_list; +extern struct call *find_call_by_sip_id(); extern char *get_single_header(); -void -handle_sip_invite(req, ess, sin) +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; @@ -37,9 +95,11 @@ 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 copylen; int rc; - /* check for existing Call-ID will go here */ /* extract called number from Request-URI */ rc = user_from_sip_uri(req->req_uri, uri_user, 12); if (rc < 0) { @@ -48,12 +108,12 @@ error_resp: rc = add_resp_basic_headers(&resp, ess, req->req_method); if (rc < 0) { error_resp_toolong: syslog(LOG_ERR, - "INVITE error response length exceeded"); + "INVITE early error response length exceeded"); return; } out_msg_finish(&resp); sip_tx_packet(&resp, sin); - return; + return; } if (uri_user[0] == '+') { if (grok_number_string(uri_user+1, 0) != 11 || @@ -156,7 +216,7 @@ if (rc < 0) goto error_resp_toolong; sip_tx_packet(&resp, sin); - return; + return; } /* SIP INVITE validation done - check if GSM service is up */ rc = connect_gsm_mtcall(); @@ -165,4 +225,149 @@ goto error_resp; } /* stateful processing begins */ + call = malloc(sizeof(struct call) + strlen(ess->call_id) + + strlen(ess->from) + strlen(ess->to) + strlen(ess->via) + + 4); + if (!call) { + syslog(LOG_CRIT, "failed malloc for incoming call!"); + start_response_out_msg(&resp, + "503 Gateway resource allocation failure"); + goto error_resp; + } + 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; + copylen = strlen(ess->to) + 1; + call->invite_to = dp; + bcopy(ess->to, dp, copylen); + dp += copylen; + 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; + /* 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: + case SIP_STATE_RINGING_PRACK: + 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_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_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; + /* 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; +}