# HG changeset patch # User Mychaela Falconia # Date 1665558241 28800 # Node ID e54b0a9e322f39234c4b593371c9d6b8eae6b050 # Parent 99fd4ae573ae85d214f8acf3ee927f5419deaf22 beginning of themwi-sip-out diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/Makefile Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,19 @@ +CC= gcc +CFLAGS= -O2 +PROG= themwi-sip-out +OBJS= call_clear.o call_list.o call_setup.o disconnect.o main.o mgw_ops.o \ + mgw_resp.o mgw_sock.o mncc_sig_in.o mncc_sig_out.o mncc_sock.o \ + readconf.o retrans.o shutdown.o sip_log.o sip_uas.o sip_udp.o uac_out.o +LIBS= ../liboutrt/liboutrt.a ../libsip/libsip.a ../libutil/libutil.a +INSTBIN=/usr/local/bin + +all: ${OBJS} + +${PROG}: ${OBJS} ${LIBS} + ${CC} ${CFLAGS} -o $@ ${OBJS} ${LIBS} + +install: + install -c -o bin -g bin -m 755 ${PROG} ${INSTBIN} + +clean: + rm -f *.o ${PROG} errs diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/call.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/call.h Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,90 @@ +/* + * In this header file we define two key structures for call state + * in themwi-sip-out: struct call exists for each call just like in + * themwi-sip-in, and struct mncc_conn exists for every connection + * to our outcall socket. + */ + +struct mncc_conn { + int fd; + struct mncc_conn *next; +}; + +#define MAX_SIP_CALL_ID 30 +#define MAX_SIP_FROM_HEADER (MAX_SIP_USER_PART + 5 + 17 + 5 + 8) +#define MAX_SIP_TO_URI (MAX_SIP_USER_PART + MAX_SIP_DEST_DOMAIN + 5) +#define MAX_SIP_TO_TAG 63 + +struct call { + /* global call list */ + struct call *next; + char sip_call_id[MAX_SIP_CALL_ID+1]; + int sip_call_exists; + /* MNCC call origin */ + struct mncc_conn *mncc; + uint32_t mncc_callref; + /* call routing info */ + char from_user[MAX_SIP_USER_PART+1]; + char from_header[MAX_SIP_FROM_HEADER+1]; + char to_uri[MAX_SIP_TO_URI+1]; + char to_tag[MAX_SIP_TO_TAG+1]; + struct sockaddr_in udp_sin; + /* PSTN side RTP info */ + struct sockaddr_in pstn_rtp_local; + struct sockaddr_in pstn_rtp_remote; + int use_pcma; + /* GSM side RTP info */ + struct sockaddr_storage gsm_rtp_osmo; + struct sockaddr_storage gsm_rtp_tmgw; + uint32_t gsm_payload_type; + uint32_t gsm_payload_msg_type; + /* state machines */ + uint32_t overall_state; + uint32_t mncc_state; + uint32_t sip_state; + uint32_t mgw_state; + uint32_t mgw_ep_id; + uint32_t mgw_xact; + uint32_t mgw_xact_id; + unsigned sip_tx_count; + time_t sip_clear_time; + int dtmf_digit; + int dtmf_pending_stop; +}; + +#define OVERALL_STATE_GSM_RTP 1 +#define OVERALL_STATE_CRCX 2 +#define OVERALL_STATE_SIP_OUT 3 +#define OVERALL_STATE_RINGING 4 +#define OVERALL_STATE_CONNECTED 5 +#define OVERALL_STATE_TEARDOWN 6 +#define OVERALL_STATE_SIP_FINISH 7 +#define OVERALL_STATE_GC 8 + +#define MNCC_STATE_MO_PROC 1 +#define MNCC_STATE_GOT_RTP 2 +#define MNCC_STATE_ALERTING 3 +#define MNCC_STATE_CONNECTED 4 +#define MNCC_STATE_DISCONNECT 5 +#define MNCC_STATE_RELEASE 6 + +#define SIP_STATE_INV_SENT 1 +#define SIP_STATE_100_RCVD 2 +#define SIP_STATE_CONNECTED 3 +#define SIP_STATE_CANCEL_SENT 4 +#define SIP_STATE_BYE_SENT 5 +#define SIP_STATE_ACCEPT_100 6 +#define SIP_STATE_ACCEPT_200 7 +#define SIP_STATE_ENDED 8 + +#define MGW_STATE_NO_EXIST 0 +#define MGW_STATE_ALLOCATED 1 +#define MGW_STATE_IBT_CONN 2 +#define MGW_STATE_COMPLETE 3 +#define MGW_STATE_HELD 4 +#define MGW_STATE_MDCX_IBT 5 +#define MGW_STATE_MDCX_CONN 6 +#define MGW_STATE_MDCX_HOLD 7 +#define MGW_STATE_MDCX_RETR 8 +#define MGW_STATE_DTMF_OP 9 +#define MGW_STATE_DELETING 10 diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/call_clear.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/call_clear.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,66 @@ +/* + * In this module we implement final clearing of calls, i.e., freeing + * of struct call, and all preliminary steps that need to happen + * toward this ultimate end state. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/out_routes.h" +#include "call.h" + +extern struct call *call_list; +extern struct timeval cur_event_time; + +void +sip_mark_end_time(call, linger) + struct call *call; + unsigned linger; +{ + call->sip_clear_time = cur_event_time.tv_sec + linger; +} + +void +check_dead_call(call) + struct call *call; +{ + if (call->overall_state != OVERALL_STATE_TEARDOWN) + return; + if (call->mncc) + return; + if (call->mgw_state != MGW_STATE_NO_EXIST) + return; + if (call->mgw_xact) + return; + if (call->sip_call_exists) + call->overall_state = OVERALL_STATE_SIP_FINISH; + else + call->overall_state = OVERALL_STATE_GC; +} + +void +clear_dead_calls() +{ + struct call *call, **pp; + + for (pp = &call_list; *pp; ) { + call = *pp; + if (call->overall_state == OVERALL_STATE_GC || + call->overall_state == OVERALL_STATE_SIP_FINISH && + call->sip_state >= SIP_STATE_ACCEPT_100 && + cur_event_time.tv_sec >= call->sip_clear_time) { + *pp = call->next; + free(call); + continue; + } + pp = &call->next; + } +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/call_list.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/call_list.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,80 @@ +/* + * In this module we implement call list management for themwi-sip-out. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/out_routes.h" +#include "call.h" + +struct call *call_list; + +struct call * +find_call_by_sip_id(sought_id) + char *sought_id; +{ + struct call *call; + + for (call = call_list; call; call = call->next) { + if (!call->sip_call_exists) + continue; + if (!strcmp(call->sip_call_id, sought_id)) + return call; + } + return 0; +} + +struct call * +find_call_by_mncc_callref(mncc, callref) + struct mncc_conn *mncc; + uint32_t callref; +{ + struct call *call; + + for (call = call_list; call; call = call->next) + if (call->mncc == mncc && call->mncc_callref == callref) + return call; + return 0; +} + +void +scan_call_list_for_timeouts(retrans, dead_sip_flag, dead_sip_time) + int *retrans, *dead_sip_flag; + time_t *dead_sip_time; +{ + struct call *call; + int got_dead_sip = 0; + + for (call = call_list; call; call = call->next) { + if (!call->sip_call_exists) + continue; + switch (call->sip_state) { + case SIP_STATE_INV_SENT: + case SIP_STATE_CANCEL_SENT: + case SIP_STATE_BYE_SENT: + *retrans = 1; + break; + case SIP_STATE_ACCEPT_100: + case SIP_STATE_ACCEPT_200: + case SIP_STATE_ENDED: + if (call->overall_state != OVERALL_STATE_SIP_FINISH) + continue; + if (got_dead_sip) { + if (call->sip_clear_time < *dead_sip_time) + *dead_sip_time = call->sip_clear_time; + } else { + got_dead_sip = 1; + *dead_sip_flag = 1; + *dead_sip_time = call->sip_clear_time; + } + } + } +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/call_setup.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/call_setup.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,171 @@ +/* + * In this module we implement our initial handling of MNCC_SETUP_IND. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/mncc.h" +#include "../include/gsm48_const.h" +#include "../include/out_routes.h" +#include "call.h" + +extern struct call *find_call_by_mncc_callref(); + +extern int block_1900_numbers; +extern struct call *call_list; + +static void +reject_mo_call(mncc, callref, cause_loc, cause_val) + struct mncc_conn *mncc; + uint32_t callref; +{ + struct gsm_mncc msg; + + bzero(&msg, sizeof(struct gsm_mncc)); + msg.msg_type = MNCC_REJ_REQ; + msg.callref = callref; + mncc_set_cause(&msg, cause_loc, cause_val); + send_mncc_to_sock(mncc, &msg, sizeof(struct gsm_mncc)); +} + +static void +send_call_proceeding(call) + struct call *call; +{ + struct gsm_mncc msg; + + bzero(&msg, sizeof(struct gsm_mncc)); + msg.msg_type = MNCC_CALL_PROC_REQ; + msg.callref = call->mncc_callref; + send_mncc_to_sock(call->mncc, &msg, sizeof(struct gsm_mncc)); +} + +void +handle_setup_ind(mncc, msg, msglen) + struct mncc_conn *mncc; + struct gsm_mncc *msg; + unsigned msglen; +{ + struct call *call; + struct sip_out_dest *dest; + struct special_num_route *special_rt; + char to_sip_user[MAX_SIP_USER_PART+1]; + int rc; + + if (msglen != sizeof(struct gsm_mncc)) { + syslog(LOG_CRIT, + "FATAL: Rx MNCC_SETUP_IND has wrong length"); + exit(1); + } + /* check for duplicates */ + call = find_call_by_mncc_callref(mncc, msg->callref); + if (call) { + syslog(LOG_ERR, "duplicate MNCC_SETUP_IND for callref 0x%x", + msg->callref); + /* drop it like OsmoMSC's mncc_builtin does */ + return; + } + if (!(msg->fields & MNCC_F_CALLED) || + !(msg->fields & MNCC_F_BEARER_CAP)) { + syslog(LOG_ERR, "rejecting MO call 0x%x: missing mandatory IE", + msg->callref); + reject_mo_call(mncc, msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INVAL_MAND_INF); + return; + } + /* route based on destination address */ + refresh_out_routes_db(); + if (msg->called.type == GSM48_TON_INTERNATIONAL) { + rc = grok_number_string(msg->called.number, 0); + if (rc < 2 || rc > MAX_E164_NUMBER) { +inv_nr_format: reject_mo_call(mncc, msg->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INV_NR_FORMAT); + return; + } + if (msg->called.number[0] == '0') + goto inv_nr_format; + if (msg->called.number[0] == '1') { + if (rc != 11) + goto inv_nr_format; + if (!is_nanp_valid_prefix(msg->called.number+1)) + goto inv_nr_format; + if (msg->called.number[1] == '9' && + msg->called.number[2] == '0' && + msg->called.number[3] == '0' && + block_1900_numbers) { +call_barred: reject_mo_call(mncc, msg->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_OP_DET_BARRING); + return; + } + } + rc = route_e164_number(msg->called.number, &dest); + if (!rc) { +no_route_to_dest: reject_mo_call(mncc, msg->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_NO_ROUTE); + return; + } + to_sip_user[0] = '+'; + strcpy(to_sip_user+1, msg->called.number); + } else { + rc = route_special_number(msg->called.number, &dest, + &special_rt); + if (!rc) + goto no_route_to_dest; + strcpy(to_sip_user, special_rt->sip_user); + } + /* validate From number */ + if (!(msg->fields & MNCC_F_CALLING)) + goto call_barred; + if (msg->calling.type != GSM48_TON_INTERNATIONAL) + goto call_barred; + if (grok_number_string(msg->calling.number, 0) != 11 || + msg->calling.number[0] != '1') + goto call_barred; + /* speech-only restriction */ + if (msg->bearer_cap.transfer != GSM48_BCAP_ITCAP_SPEECH || + msg->bearer_cap.mode != GSM48_BCAP_TMOD_CIRCUIT) { + syslog(LOG_ERR, "rejecting MO call 0x%x: bad bearer cap", + msg->callref); + reject_mo_call(mncc, msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); + return; + } + /* TMGW must be up and running */ + rc = connect_tmgw_socket(); + if (rc < 0) { + reject_mo_call(mncc, msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_NETWORK_OOO); + return; + } + /* allocate struct call and being stateful processing */ + call = malloc(sizeof(struct call)); + if (!call) { + syslog(LOG_CRIT, "failed malloc for outbound call!"); + reject_mo_call(mncc, msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + return; + } + bzero(call, sizeof(struct call)); + call->mncc = mncc; + call->mncc_callref = msg->callref; + call->from_user[0] = '+'; + strcpy(call->from_user+1, msg->calling.number); + sprintf(call->to_uri, "sip:%s@%s", to_sip_user, dest->domain); + bcopy(&dest->sin, &call->udp_sin, sizeof(struct sockaddr_in)); + call->next = call_list; + call_list = call; + send_call_proceeding(call); + call->overall_state = OVERALL_STATE_GSM_RTP; + call->mncc_state = MNCC_STATE_MO_PROC; +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/disconnect.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/disconnect.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,88 @@ +/* + * In this module we implement call disconnection and clearing procedures. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/mncc.h" +#include "../include/gsm48_const.h" +#include "../include/out_routes.h" +#include "call.h" + +extern unsigned sip_linger_timeout; + +void +disconnect_mncc(call, cause_loc, cause_val) + struct call *call; +{ + struct gsm_mncc msg; + + if (!call->mncc) + return; + switch (call->mncc_state) { + case MNCC_STATE_DISCONNECT: + case MNCC_STATE_RELEASE: + return; + } + bzero(&msg, sizeof(struct gsm_mncc)); + msg.msg_type = MNCC_DISC_REQ; + msg.callref = call->mncc_callref; + mncc_set_cause(&msg, cause_loc, cause_val); + send_mncc_to_sock(call->mncc, &msg, sizeof(struct gsm_mncc)); + call->mncc_state = MNCC_STATE_DISCONNECT; +} + +void +disconnect_tmgw(call) + struct call *call; +{ + switch (call->mgw_state) { + case MGW_STATE_NO_EXIST: + case MGW_STATE_DELETING: + case MGW_STATE_MDCX_IBT: + case MGW_STATE_MDCX_CONN: + case MGW_STATE_MDCX_HOLD: + case MGW_STATE_MDCX_RETR: + case MGW_STATE_DTMF_OP: + return; + case MGW_STATE_ALLOCATED: + case MGW_STATE_IBT_CONN: + case MGW_STATE_COMPLETE: + case MGW_STATE_HELD: + tmgw_send_dlcx(call); + return; + default: + syslog(LOG_CRIT, + "FATAL: invalid MGW state 0x%x in disconnect_tmgw()", + call->mgw_state); + exit(1); + } +} + +void +disconnect_sip(call) + struct call *call; +{ + if (!call->sip_call_exists) + return; + switch (call->sip_state) { + case SIP_STATE_INV_SENT: + call->sip_state = SIP_STATE_ACCEPT_100; + sip_mark_end_time(call, sip_linger_timeout); + break; + case SIP_STATE_100_RCVD: + initiate_sip_cancel(call); + break; + case SIP_STATE_CONNECTED: + initiate_bye(call); + break; + } +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/main.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,119 @@ +/* + * Main module for themwi-sip-out. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/out_routes.h" +#include "call.h" + +extern unsigned cfg_retrans_timeout; + +extern int mgw_socket, mgw_is_connected; +extern int sip_socket, mncc_listener; +extern struct mncc_conn *mncc_conn_list; + +static int max_fd; + +struct timeval cur_event_time; + +update_max_fd(newfd) +{ + if (newfd > max_fd) + max_fd = newfd; +} + +main(argc, argv) + char **argv; +{ + fd_set fds; + struct mncc_conn *conn, **connp; + int rc, need_retrans, dead_sip_flag; + struct timeval timeout; + time_t dead_sip_time; + + openlog("themwi-sip-out", 0, LOG_LOCAL5); + read_config_file(); + if (read_out_routes_db() < 0) { + fprintf(stderr, "error reading out-routes.bin database\n"); + exit(1); + } + if (create_outcall_socket() < 0) { + fprintf(stderr, "error creating outcall socket\n"); + exit(1); + } + if (open_sip_udp_socket() < 0) { + fprintf(stderr, "error opening SIP UDP socket\n"); + exit(1); + } + if (argv[1]) { + rc = open_sip_log_file(argv[1]); + if (rc < 0) + exit(1); /* error msg already printed */ + } + signal(SIGPIPE, SIG_IGN); + /* main select loop */ + for (;;) { + FD_ZERO(&fds); + FD_SET(sip_socket, &fds); + FD_SET(mncc_listener, &fds); + if (mgw_is_connected) + FD_SET(mgw_socket, &fds); + for (connp = &mncc_conn_list; conn = *connp; ) { + if (conn->fd < 0) { + *connp = conn->next; + free(conn); + continue; + } + FD_SET(conn->fd, &fds); + connp = &conn->next; + } + need_retrans = dead_sip_flag = 0; + scan_call_list_for_timeouts(&need_retrans, &dead_sip_flag, + &dead_sip_time); + if (need_retrans) { + timeout.tv_sec = cfg_retrans_timeout / 1000; + timeout.tv_usec = (cfg_retrans_timeout % 1000) * 1000; + rc = select(max_fd+1, &fds, 0, 0, &timeout); + } else if (dead_sip_flag) { + if (cur_event_time.tv_sec >= dead_sip_time) + timeout.tv_sec = 0; + else + timeout.tv_sec = + dead_sip_time - cur_event_time.tv_sec; + timeout.tv_usec = 0; + rc = select(max_fd+1, &fds, 0, 0, &timeout); + } else + rc = select(max_fd+1, &fds, 0, 0, 0); + if (rc < 0) { + if (errno == EINTR) + continue; + syslog(LOG_CRIT, "select: %m"); + exit(1); + } + gettimeofday(&cur_event_time, 0); + if (rc) { + if (FD_ISSET(mncc_listener, &fds)) + mncc_listener_select(); + for (conn = mncc_conn_list; conn; conn = conn->next) + if (FD_ISSET(conn->fd, &fds)) + mncc_socket_select(conn); + if (FD_ISSET(sip_socket, &fds)) + sip_socket_select(); + if (mgw_is_connected && FD_ISSET(mgw_socket, &fds)) + mgw_socket_select(); + } else if (need_retrans) + run_periodic_retrans(); + clear_dead_calls(); + } +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/mgw_ops.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/mgw_ops.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,172 @@ +/* + * In this module we implement all transactions from themwi-sip-out + * toward themwi-mgw. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/tmgw_ctrl.h" +#include "../include/tmgw_const.h" +#include "../include/out_routes.h" +#include "call.h" + +extern struct call *call_list; + +struct call * +find_call_with_mgw_xact(xact_id) + uint32_t xact_id; +{ + struct call *call; + + for (call = call_list; call; call = call->next) + if (call->mgw_xact && call->mgw_xact_id == xact_id) + return call; + return 0; +} + +uint32_t +get_new_tmgw_xact_id() +{ + static uint32_t next_xact_id; + + for (;;) { + next_xact_id++; + if (!find_call_with_mgw_xact(next_xact_id)) + return next_xact_id; + } +} + +void +tmgw_send_crcx(call) + struct call *call; +{ + struct tmgw_ctrl_req req; + + bzero(&req, sizeof req); + req.opcode = TMGW_CTRL_OP_CRCX; + req.transact_ref = get_new_tmgw_xact_id(); + req.ep_id = TMGW_EP_TYPE_GATEWAY; + req.setup_mask = TMGW_CTRL_MASK_GSM_CONN; + bcopy(&call->gsm_rtp_osmo, &req.gsm_addr, + sizeof(struct sockaddr_storage)); + req.gsm_payload_type = call->gsm_payload_type; + req.gsm_payload_msg_type = call->gsm_payload_msg_type; + send_req_to_tmgw(&req); + call->mgw_xact = TMGW_CTRL_OP_CRCX; + call->mgw_xact_id = req.transact_ref; +} + +void +tmgw_send_mdcx_connect(call, ibt) + struct call *call; +{ + struct tmgw_ctrl_req req; + + bzero(&req, sizeof req); + req.opcode = TMGW_CTRL_OP_MDCX; + req.transact_ref = get_new_tmgw_xact_id(); + req.ep_id = call->mgw_ep_id; + req.setup_mask = TMGW_CTRL_MASK_PSTN_CONN | TMGW_CTRL_MASK_FWD_MODE; + bcopy(&call->pstn_rtp_remote, &req.pstn_addr, + sizeof(struct sockaddr_in)); + req.pstn_payload_type = + call->use_pcma ? PSTN_CODEC_PCMA : PSTN_CODEC_PCMU; + req.fwd_mode = ibt ? TMGW_FWD_MODE_RECVONLY : TMGW_FWD_MODE_SENDRECV; + send_req_to_tmgw(&req); + call->mgw_state = ibt ? MGW_STATE_MDCX_IBT : MGW_STATE_MDCX_CONN; + call->mgw_xact = TMGW_CTRL_OP_MDCX; + call->mgw_xact_id = req.transact_ref; +} + +void +tmgw_send_mdcx_hold(call) + struct call *call; +{ + struct tmgw_ctrl_req req; + + bzero(&req, sizeof req); + req.opcode = TMGW_CTRL_OP_MDCX; + req.transact_ref = get_new_tmgw_xact_id(); + req.ep_id = call->mgw_ep_id; + req.setup_mask = TMGW_CTRL_MASK_FWD_MODE; + req.fwd_mode = TMGW_FWD_MODE_INACTIVE; + send_req_to_tmgw(&req); + call->mgw_state = MGW_STATE_MDCX_HOLD; + call->mgw_xact = TMGW_CTRL_OP_MDCX; + call->mgw_xact_id = req.transact_ref; +} + +void +tmgw_send_mdcx_retrieve(call) + struct call *call; +{ + struct tmgw_ctrl_req req; + + bzero(&req, sizeof req); + req.opcode = TMGW_CTRL_OP_MDCX; + req.transact_ref = get_new_tmgw_xact_id(); + req.ep_id = call->mgw_ep_id; + req.setup_mask = TMGW_CTRL_MASK_FWD_MODE; + req.fwd_mode = TMGW_FWD_MODE_SENDRECV; + send_req_to_tmgw(&req); + call->mgw_state = MGW_STATE_MDCX_RETR; + call->mgw_xact = TMGW_CTRL_OP_MDCX; + call->mgw_xact_id = req.transact_ref; +} + +void +tmgw_send_dlcx(call) + struct call *call; +{ + struct tmgw_ctrl_req req; + + bzero(&req, sizeof req); + req.opcode = TMGW_CTRL_OP_DLCX; + req.transact_ref = get_new_tmgw_xact_id(); + req.ep_id = call->mgw_ep_id; + send_req_to_tmgw(&req); + call->mgw_state = MGW_STATE_DELETING; + call->mgw_xact = TMGW_CTRL_OP_DLCX; + call->mgw_xact_id = req.transact_ref; +} + +void +tmgw_send_dtmf_start(call) + struct call *call; +{ + struct tmgw_ctrl_req req; + + bzero(&req, sizeof req); + req.opcode = TMGW_CTRL_OP_DTMF_START; + req.transact_ref = get_new_tmgw_xact_id(); + req.ep_id = call->mgw_ep_id; + req.fwd_mode = call->dtmf_digit; + send_req_to_tmgw(&req); + call->mgw_state = MGW_STATE_DTMF_OP; + call->mgw_xact = TMGW_CTRL_OP_DTMF_START; + call->mgw_xact_id = req.transact_ref; +} + +void +tmgw_send_dtmf_stop(call) + struct call *call; +{ + struct tmgw_ctrl_req req; + + bzero(&req, sizeof req); + req.opcode = TMGW_CTRL_OP_DTMF_STOP; + req.transact_ref = get_new_tmgw_xact_id(); + req.ep_id = call->mgw_ep_id; + send_req_to_tmgw(&req); + call->mgw_state = MGW_STATE_DTMF_OP; + call->mgw_xact = TMGW_CTRL_OP_DTMF_STOP; + call->mgw_xact_id = req.transact_ref; +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/mgw_resp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/mgw_resp.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,212 @@ +/* + * In this module we implement our handling of all responses + * from themwi-mgw. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/gsm48_const.h" +#include "../include/tmgw_ctrl.h" +#include "../include/tmgw_const.h" +#include "../include/out_routes.h" +#include "call.h" + +extern struct call *find_call_with_mgw_xact(); + +static void +crcx_response(call, msg) + struct call *call; + struct tmgw_ctrl_resp *msg; +{ + int cause; + + if (msg->res == TMGW_RESP_OK) { + call->mgw_state = MGW_STATE_ALLOCATED; + call->mgw_ep_id = msg->ep_id; + bcopy(&msg->gsm_addr, &call->gsm_rtp_tmgw, + sizeof(struct sockaddr_storage)); + bcopy(&msg->pstn_addr, &call->pstn_rtp_local, + sizeof(struct sockaddr_in)); + switch (call->overall_state) { + case OVERALL_STATE_CRCX: + send_rtp_connect(call); + start_sip_out(call); + return; + case OVERALL_STATE_TEARDOWN: + tmgw_send_dlcx(call); + return; + default: + bad_state: + syslog(LOG_CRIT, + "FATAL: invalid overall state 0x%x on CRCX response", + call->overall_state); + exit(1); + } + } else { + switch (call->overall_state) { + case OVERALL_STATE_CRCX: + call->overall_state = OVERALL_STATE_TEARDOWN; + switch (msg->res) { + case TMGW_RESP_ERR_RSRC: + cause = GSM48_CC_CAUSE_RESOURCE_UNAVAIL; + break; + case TMGW_RESP_ERR_NOTSUP: + cause = GSM48_CC_CAUSE_BEARER_CA_UNAVAIL; + break; + default: + cause = GSM48_CC_CAUSE_PROTO_ERR; + } + disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, cause); + return; + case OVERALL_STATE_TEARDOWN: + check_dead_call(call); + return; + default: + goto bad_state; + } + } +} + +static void +mdcx_response(call, msg) + struct call *call; + struct tmgw_ctrl_resp *msg; +{ + if (call->overall_state == OVERALL_STATE_TEARDOWN) { + tmgw_send_dlcx(call); + return; + } + if (msg->res != TMGW_RESP_OK) { + call->overall_state = OVERALL_STATE_TEARDOWN; + tmgw_send_dlcx(call); + if (msg->res == TMGW_RESP_ERR_RSRC && + (call->mgw_state == MGW_STATE_MDCX_IBT || + call->mgw_state == MGW_STATE_MDCX_CONN)) + disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + else + disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_NETWORK_OOO); + disconnect_sip(call); + return; + } + switch (call->mgw_state) { + case MGW_STATE_MDCX_IBT: + call->mgw_state = MGW_STATE_IBT_CONN; + mncc_signal_ibt_ring(call); + if (call->sip_state == SIP_STATE_CONNECTED) + tmgw_send_mdcx_connect(call, 0); + return; + case MGW_STATE_MDCX_CONN: + call->mgw_state = MGW_STATE_COMPLETE; + mncc_signal_answer_sup(call); + return; + case MGW_STATE_MDCX_HOLD: + call->mgw_state = MGW_STATE_HELD; + mncc_send_hold_ack(call); + return; + case MGW_STATE_MDCX_RETR: + call->mgw_state = MGW_STATE_COMPLETE; + mncc_send_retrieve_ack(call); + return; + default: + syslog(LOG_CRIT, + "FATAL: invalid MGW state 0x%x on MDCX response", + call->mgw_state); + exit(1); + } +} + +static void +dlcx_response(call, msg) + struct call *call; + struct tmgw_ctrl_resp *msg; +{ + if (msg->res != TMGW_RESP_OK) { + syslog(LOG_CRIT, "FATAL: TMGW DLCX failed with code 0x%x", + msg->res); + exit(1); + } + call->mgw_state = MGW_STATE_NO_EXIST; + check_dead_call(call); +} + +static void +dtmf_start_response(call, msg) + struct call *call; + struct tmgw_ctrl_resp *msg; +{ + if (call->overall_state == OVERALL_STATE_TEARDOWN) { + tmgw_send_dlcx(call); + return; + } + if (msg->res == TMGW_RESP_OK) + mncc_dtmf_start_ok(call); + else + mncc_dtmf_start_err(call); + if (call->dtmf_pending_stop) + tmgw_send_dtmf_stop(call); + else + call->mgw_state = MGW_STATE_COMPLETE; +} + +static void +dtmf_stop_response(call, msg) + struct call *call; + struct tmgw_ctrl_resp *msg; +{ + if (call->overall_state == OVERALL_STATE_TEARDOWN) { + tmgw_send_dlcx(call); + return; + } + mncc_dtmf_stop_ok(call); + call->mgw_state = MGW_STATE_COMPLETE; + call->dtmf_pending_stop = 0; +} + +void +process_tmgw_response(msg) + struct tmgw_ctrl_resp *msg; +{ + struct call *call; + unsigned opc; + + call = find_call_with_mgw_xact(msg->transact_ref); + if (!call) { + syslog(LOG_CRIT, + "FATAL: response from TMGW xact 0x%x does not match any call", + msg->transact_ref); + exit(1); + } + opc = call->mgw_xact; + call->mgw_xact = 0; + switch (opc) { + case TMGW_CTRL_OP_CRCX: + crcx_response(call, msg); + return; + case TMGW_CTRL_OP_MDCX: + mdcx_response(call, msg); + return; + case TMGW_CTRL_OP_DLCX: + dlcx_response(call, msg); + return; + case TMGW_CTRL_OP_DTMF_START: + dtmf_start_response(call, msg); + return; + case TMGW_CTRL_OP_DTMF_STOP: + dtmf_stop_response(call, msg); + return; + default: + syslog(LOG_CRIT, + "FATAL: invalid opcode 0x%x in call->msg_xact", opc); + exit(1); + } +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/mgw_sock.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/mgw_sock.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,70 @@ +/* + * In this module we implement our local socket interface to themwi-mgw. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../include/tmgw_ctrl.h" + +static char tmgw_socket_pathname[] = "/var/gsm/tmgw_socket"; + +int mgw_socket, mgw_is_connected; + +connect_tmgw_socket() +{ + struct sockaddr_un sa; + unsigned sa_len; + int rc; + + if (mgw_is_connected) + return(0); + mgw_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (mgw_socket < 0) { + syslog(LOG_CRIT, "socket(AF_UNIX, SOCK_SEQPACKET, 0): %m"); + return(-1); + } + fill_sockaddr_un(tmgw_socket_pathname, &sa, &sa_len); + rc = connect(mgw_socket, (struct sockaddr *) &sa, sa_len); + if (rc < 0) { + syslog(LOG_ERR, "connect to %s: %m", tmgw_socket_pathname); + close(mgw_socket); + return(-1); + } + update_max_fd(mgw_socket); + mgw_is_connected = 1; + return(0); +} + +void +mgw_socket_select() +{ + struct tmgw_ctrl_resp msg; + int rc; + + rc = recv(mgw_socket, &msg, sizeof msg, 0); + if (rc <= 0) { + syslog(LOG_ERR, "themwi-mgw socket disconnected"); + close(mgw_socket); + mgw_is_connected = 0; + shutdown_mgw_conn(); + return; + } + if (rc != sizeof(struct tmgw_ctrl_resp)) { + syslog(LOG_CRIT, + "response packet from TMGW has wrong length: %d bytes", + rc); + exit(1); + } + process_tmgw_response(&msg); +} + +send_req_to_tmgw(msg) + struct tmgw_ctrl_req *msg; +{ + return send(mgw_socket, msg, sizeof(struct tmgw_ctrl_req), 0); +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/mncc_sig_in.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/mncc_sig_in.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,214 @@ +/* + * In this module we implement our handling of call control messages + * from OsmoMSC relayed to us via themwi-mncc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/mncc.h" +#include "../include/gsm48_const.h" +#include "../include/out_routes.h" +#include "call.h" + +extern struct call *find_call_by_mncc_callref(); + +static void +handle_disconnect_ind(call, msg) + struct call *call; + struct gsm_mncc *msg; +{ + /* release back to MNCC */ + msg->msg_type = MNCC_REL_REQ; + send_mncc_to_sock(call->mncc, msg, sizeof(struct gsm_mncc)); + call->mncc_state = MNCC_STATE_RELEASE; + /* signal disconnect to SIP */ + call->overall_state = OVERALL_STATE_TEARDOWN; + disconnect_sip(call); + disconnect_tmgw(call); +} + +static void +handle_final_release(call, msg) + struct call *call; + struct gsm_mncc *msg; +{ + /* MNCC call leg is gone */ + call->mncc = 0; + /* signal disconnect to SIP */ + call->overall_state = OVERALL_STATE_TEARDOWN; + disconnect_sip(call); + disconnect_tmgw(call); + check_dead_call(call); +} + +static void +handle_dtmf_start(call, msg) + struct call *call; + struct gsm_mncc *msg; +{ + if (!(msg->fields & MNCC_F_KEYPAD) || + !is_valid_dtmf_digit(msg->keypad)) { + msg->msg_type = MNCC_START_DTMF_REJ; + mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INVAL_MAND_INF); + send_mncc_to_sock(call->mncc, msg, sizeof(struct gsm_mncc)); + return; + } + if (call->mncc_state != MNCC_STATE_CONNECTED || + call->mgw_state != MGW_STATE_COMPLETE) { + msg->msg_type = MNCC_START_DTMF_REJ; + mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_MSGTYPE_INCOMPAT); + send_mncc_to_sock(call->mncc, msg, sizeof(struct gsm_mncc)); + return; + } + call->dtmf_digit = msg->keypad; + tmgw_send_dtmf_start(call); +} + +static void +handle_dtmf_stop(call, msg) + struct call *call; + struct gsm_mncc *msg; +{ + if (call->mncc_state != MNCC_STATE_CONNECTED) + return; + if (call->mgw_state == MGW_STATE_COMPLETE) + tmgw_send_dtmf_stop(call); + else if (call->mgw_state == MGW_STATE_DTMF_OP) + call->dtmf_pending_stop = 1; + else { + /* dummy OK response */ + msg->msg_type = MNCC_STOP_DTMF_RSP; + send_mncc_to_sock(call->mncc, msg, sizeof(struct gsm_mncc)); + } +} + +static void +handle_call_hold(call, msg) + struct call *call; + struct gsm_mncc *msg; +{ + if (call->mncc_state != MNCC_STATE_CONNECTED || + call->mgw_state != MGW_STATE_COMPLETE) { + msg->msg_type = MNCC_HOLD_REJ; + mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_MSGTYPE_INCOMPAT); + send_mncc_to_sock(call->mncc, msg, sizeof(struct gsm_mncc)); + return; + } + tmgw_send_mdcx_hold(call); +} + +static void +handle_call_retrieve(call, msg) + struct call *call; + struct gsm_mncc *msg; +{ + if (call->mncc_state != MNCC_STATE_CONNECTED || + call->mgw_state != MGW_STATE_HELD) { + msg->msg_type = MNCC_RETRIEVE_REJ; + mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_MSGTYPE_INCOMPAT); + send_mncc_to_sock(call->mncc, msg, sizeof(struct gsm_mncc)); + return; + } + send_rtp_connect(call); + tmgw_send_mdcx_retrieve(call); +} + +void +handle_mncc_signal(mncc, msg, msglen) + struct mncc_conn *mncc; + struct gsm_mncc *msg; + unsigned msglen; +{ + struct call *call; + + if (msglen != sizeof(struct gsm_mncc)) { + syslog(LOG_CRIT, + "FATAL: Rx MNCC message type 0x%x has wrong length", + msg->msg_type); + exit(1); + } + call = find_call_by_mncc_callref(mncc, msg->callref); + if (!call) { + syslog(LOG_CRIT, + "error: Rx MNCC message type 0x%x has invalid callref 0x%x", + msg->msg_type, msg->callref); + exit(1); + } + switch (msg->msg_type) { + case MNCC_DISC_IND: + handle_disconnect_ind(call, msg); + return; + case MNCC_REL_IND: + case MNCC_REL_CNF: + handle_final_release(call, msg); + return; + case MNCC_START_DTMF_IND: + handle_dtmf_start(call, msg); + return; + case MNCC_STOP_DTMF_IND: + handle_dtmf_stop(call, msg); + return; + case MNCC_MODIFY_IND: + msg->msg_type = MNCC_MODIFY_REJ; + mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); + send_mncc_to_sock(mncc, msg, sizeof(struct gsm_mncc)); + return; + case MNCC_HOLD_IND: + handle_call_hold(call, msg); + return; + case MNCC_RETRIEVE_IND: + handle_call_retrieve(call, msg); + return; + } +} + +void +handle_rtp_create(mncc, msg, msglen) + struct mncc_conn *mncc; + struct gsm_mncc_rtp *msg; + unsigned msglen; +{ + struct call *call; + + if (msglen != sizeof(struct gsm_mncc_rtp)) { + syslog(LOG_CRIT, + "FATAL: Rx MNCC message type 0x%x has wrong length", + msg->msg_type); + exit(1); + } + call = find_call_by_mncc_callref(mncc, msg->callref); + if (!call) { + syslog(LOG_CRIT, + "error: Rx MNCC message type 0x%x has invalid callref 0x%x", + msg->msg_type, msg->callref); + exit(1); + } + /* we need to be in the right state */ + if (call->mncc_state != MNCC_STATE_MO_PROC) { + syslog(LOG_ERR, + "duplicate MNCC_RTP_CREATE for MO callref 0x%x, ignoring", + msg->callref); + return; + } + /* save Osmocom network RTP information */ + bcopy(&msg->addr, &call->gsm_rtp_osmo, sizeof(struct sockaddr_storage)); + call->gsm_payload_type = msg->payload_type; + call->gsm_payload_msg_type = msg->payload_msg_type; + /* move forward */ + call->mncc_state = MNCC_STATE_GOT_RTP; + call->overall_state = OVERALL_STATE_CRCX; + tmgw_send_crcx(call); +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/mncc_sig_out.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/mncc_sig_out.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,145 @@ +/* + * In this module we implement functions that send MNCC messages + * to OsmoMSC via themwi-mncc, to be called by SIP and TMGW events. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/mncc.h" +#include "../include/gsm48_const.h" +#include "../include/out_routes.h" +#include "call.h" + +void +mncc_signal_alerting(call) + struct call *call; +{ + struct gsm_mncc msg; + + if (call->mncc_state == MNCC_STATE_ALERTING) + return; + bzero(&msg, sizeof(struct gsm_mncc)); + msg.msg_type = MNCC_ALERT_REQ; + msg.callref = call->mncc_callref; + send_mncc_to_sock(call->mncc, &msg, sizeof(struct gsm_mncc)); + call->mncc_state = MNCC_STATE_ALERTING; +} + +void +mncc_signal_ibt_ring(call) + struct call *call; +{ + struct gsm_mncc msg; + + bzero(&msg, sizeof(struct gsm_mncc)); + if (call->overall_state == OVERALL_STATE_RINGING && + call->mncc_state != MNCC_STATE_ALERTING) { + msg.msg_type = MNCC_ALERT_REQ; + call->mncc_state = MNCC_STATE_ALERTING; + } else + msg.msg_type = MNCC_PROGRESS_REQ; + msg.callref = call->mncc_callref; + msg.fields |= MNCC_F_PROGRESS; + msg.progress.coding = GSM48_CAUSE_CODING_GSM; + msg.progress.location = GSM48_CAUSE_LOC_NET_BEYOND; + msg.progress.descr = GSM48_PROGR_IN_BAND_AVAIL; + send_mncc_to_sock(call->mncc, &msg, sizeof(struct gsm_mncc)); +} + +void +mncc_signal_answer_sup(call) + struct call *call; +{ + struct gsm_mncc msg; + + bzero(&msg, sizeof(struct gsm_mncc)); + msg.msg_type = MNCC_SETUP_RSP; + msg.callref = call->mncc_callref; + send_mncc_to_sock(call->mncc, &msg, sizeof(struct gsm_mncc)); + call->mncc_state = MNCC_STATE_CONNECTED; +} + +void +mncc_dtmf_start_ok(call) + struct call *call; +{ + struct gsm_mncc msg; + + bzero(&msg, sizeof(struct gsm_mncc)); + msg.msg_type = MNCC_START_DTMF_RSP; + msg.callref = call->mncc_callref; + msg.fields |= MNCC_F_KEYPAD; + msg.keypad = call->dtmf_digit; + send_mncc_to_sock(call->mncc, &msg, sizeof(struct gsm_mncc)); +} + +void +mncc_dtmf_start_err(call) + struct call *call; +{ + struct gsm_mncc msg; + + bzero(&msg, sizeof(struct gsm_mncc)); + msg.msg_type = MNCC_START_DTMF_REJ; + msg.callref = call->mncc_callref; + mncc_set_cause(&msg, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_PROTO_ERR); + send_mncc_to_sock(call->mncc, &msg, sizeof(struct gsm_mncc)); +} + +void +mncc_dtmf_stop_ok(call) + struct call *call; +{ + struct gsm_mncc msg; + + bzero(&msg, sizeof(struct gsm_mncc)); + msg.msg_type = MNCC_STOP_DTMF_RSP; + msg.callref = call->mncc_callref; + send_mncc_to_sock(call->mncc, &msg, sizeof(struct gsm_mncc)); +} + +void +mncc_send_hold_ack(call) + struct call *call; +{ + struct gsm_mncc msg; + + bzero(&msg, sizeof(struct gsm_mncc)); + msg.msg_type = MNCC_HOLD_CNF; + msg.callref = call->mncc_callref; + send_mncc_to_sock(call->mncc, &msg, sizeof(struct gsm_mncc)); +} + +void +mncc_send_retrieve_ack(call) + struct call *call; +{ + struct gsm_mncc msg; + + bzero(&msg, sizeof(struct gsm_mncc)); + msg.msg_type = MNCC_RETRIEVE_CNF; + msg.callref = call->mncc_callref; + send_mncc_to_sock(call->mncc, &msg, sizeof(struct gsm_mncc)); +} + +void +send_rtp_connect(call) + struct call *call; +{ + struct gsm_mncc_rtp rtp; + + bzero(&rtp, sizeof(struct gsm_mncc_rtp)); + rtp.msg_type = MNCC_RTP_CONNECT; + rtp.callref = call->mncc_callref; + bcopy(&call->gsm_rtp_tmgw, &rtp.addr, sizeof(struct sockaddr_storage)); + send_mncc_to_sock(call->mncc, &rtp, sizeof(struct gsm_mncc_rtp)); +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/mncc_sock.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/mncc_sock.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,153 @@ +/* + * In this module we implement the MNCC socket interface to themwi-sip-out. + * We have an outcall socket on which we accept connections, and each + * accepted connection becomes a struct mncc_conn for us. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/mncc.h" +#include "../include/out_routes.h" +#include "call.h" + +static char outcall_socket_pathname[] = "/var/gsm/outcall_socket"; + +int mncc_listener; +struct mncc_conn *mncc_conn_list; + +create_outcall_socket() +{ + struct sockaddr_un sa; + unsigned sa_len; + int rc; + + mncc_listener = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (mncc_listener < 0) { + syslog(LOG_CRIT, "socket(AF_UNIX, SOCK_SEQPACKET, 0): %m"); + return(-1); + } + unlink(outcall_socket_pathname); + fill_sockaddr_un(outcall_socket_pathname, &sa, &sa_len); + rc = bind(mncc_listener, (struct sockaddr *) &sa, sa_len); + if (rc < 0) { + syslog(LOG_ERR, "bind to %s: %m", outcall_socket_pathname); + return(-1); + } + rc = listen(mncc_listener, 3); + if (rc < 0) { + syslog(LOG_CRIT, "listen on UNIX socket: %m"); + return(-1); + } + chmod(outcall_socket_pathname, 0775); + update_max_fd(mncc_listener); + return(0); +} + +void +mncc_listener_select() +{ + struct sockaddr_un sa; + socklen_t sa_len; + int fd; + struct mncc_conn *conn; + + sa_len = sizeof sa; + fd = accept(mncc_listener, (struct sockaddr *) &sa, &sa_len); + if (fd < 0) { + syslog(LOG_CRIT, "accept on UNIX socket: %m"); + exit(1); + } + conn = malloc(sizeof(struct mncc_conn)); + if (!conn) { + syslog(LOG_CRIT, "malloc for outcall socket conn: %m"); + close(fd); + return; + } + syslog(LOG_INFO, "new outcall socket connection"); + conn->fd = fd; + conn->next = mncc_conn_list; + mncc_conn_list = conn; + update_max_fd(fd); +} + +static void +dispatch_mncc_msg(mncc, msg, msglen) + struct mncc_conn *mncc; + union mncc_msg *msg; + unsigned msglen; +{ + switch (msg->msg_type) { + case MNCC_SETUP_IND: + handle_setup_ind(mncc, msg, msglen); + return; + case MNCC_SETUP_COMPL_IND: + case MNCC_NOTIFY_IND: + case MNCC_DISC_IND: + case MNCC_FACILITY_IND: + case MNCC_START_DTMF_IND: + case MNCC_STOP_DTMF_IND: + case MNCC_MODIFY_IND: + case MNCC_HOLD_IND: + case MNCC_RETRIEVE_IND: + case MNCC_USERINFO_IND: + case MNCC_REL_IND: + case MNCC_REL_CNF: + handle_mncc_signal(mncc, msg, msglen); + return; + case MNCC_RTP_CREATE: + handle_rtp_create(mncc, msg, msglen); + return; + case MNCC_RTP_CONNECT: + syslog(LOG_ERR, "MNCC_RTP_CONNECT error from OsmoMSC"); + return; + case MNCC_RTP_FREE: + syslog(LOG_ERR, "MNCC_RTP_FREE bogon from OsmoMSC"); + return; + default: + syslog(LOG_CRIT, + "FATAL: received unexpected MNCC message type 0x%x", + msg->msg_type); + exit(1); + } +} + +void +mncc_socket_select(conn) + struct mncc_conn *conn; +{ + union mncc_msg msg; + int rc; + + rc = recv(conn->fd, &msg, sizeof msg, 0); + if (rc <= 0) { + syslog(LOG_ERR, "outcall socket disconnected"); + close(conn->fd); + conn->fd = -1; + shutdown_mncc_socket(conn); + return; + } + if (rc < 4) { + syslog(LOG_CRIT, "short read from MNCC socket: %d bytes", rc); + exit(1); + } + dispatch_mncc_msg(conn, &msg, rc); +} + +send_mncc_to_sock(mncc, msg, msglen) + struct mncc_conn *mncc; + union mncc_msg *msg; + unsigned msglen; +{ + return send(mncc->fd, msg, msglen, 0); +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/readconf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/readconf.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,237 @@ +/* + * In this module we implement the reading of /var/gsm/themwi-sip-out.cfg: + * the main settings are bind-ip and bind-port, but we also have some + * optional config settings. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct in_addr sip_bind_ip; +unsigned sip_bind_port; +unsigned cfg_retrans_timeout = 500; +unsigned cfg_retrans_count = 10; +unsigned max_forwards = 70; +unsigned sip_linger_timeout = 10; +unsigned sip_linger_invite_err = 10; +unsigned sip_linger_gotbye = 30; +unsigned sip_linger_bye_out_ok = 5; +unsigned sip_linger_bye_out_err = 180; +int block_1900_numbers = 1; + +static char config_file_pathname[] = "/var/gsm/themwi-sip-out.cfg"; + +struct parse_state { + int lineno; + int set_mask; +}; + +static void +require_one_arg(st, kw, arg) + struct parse_state *st; + char *kw, *arg; +{ + char *cp; + + if (*arg == '\0' || *arg == '#') { +inv_syntax: fprintf(stderr, + "%s line %d: %s setting requires one argument\n", + config_file_pathname, st->lineno, kw); + exit(1); + } + for (cp = arg; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + while (isspace(*cp)) + cp++; + if (*cp != '\0' && *cp != '#') + goto inv_syntax; +} + +static void +handle_ip(st, kw, arg, var) + struct parse_state *st; + char *kw, *arg; + struct in_addr *var; +{ + require_one_arg(st, kw, arg); + var->s_addr = inet_addr(arg); + if (var->s_addr == INADDR_NONE) { + fprintf(stderr, + "%s line %d: invalid IP address argument \"%s\"\n", + config_file_pathname, st->lineno, arg); + exit(1); + } +} + +static void +handle_num(st, kw, arg, var) + struct parse_state *st; + char *kw, *arg; + unsigned *var; +{ + char *endp; + + require_one_arg(st, kw, arg); + *var = strtoul(arg, &endp, 10); + if (*endp) { + fprintf(stderr, "%s line %d: invalid numeric argument \"%s\"\n", + config_file_pathname, st->lineno, arg); + exit(1); + } +} + +static void +handle_bool(st, kw, arg, var) + struct parse_state *st; + char *kw, *arg; + int *var; +{ + require_one_arg(st, kw, arg); + if (!strcmp(arg, "true") || !strcmp(arg, "on") || !strcmp(arg, "yes") + || !strcmp(arg, "1")) { + *var = 1; + return; + } + if (!strcmp(arg, "false") || !strcmp(arg, "off") || !strcmp(arg, "no") + || !strcmp(arg, "0")) { + *var = 0; + return; + } + fprintf(stderr, "%s line %d: invalid boolean argument \"%s\"\n", + config_file_pathname, st->lineno, arg); + exit(1); +} + +static void +handle_retrans_conf(st, kw, arg) + struct parse_state *st; + char *kw, *arg; +{ + char *cp = arg; + + if (!isdigit(*cp)) { +inv: fprintf(stderr, + "%s line %d: %s setting requires two numeric arguments\n", + config_file_pathname, st->lineno, kw); + exit(1); + } + cfg_retrans_timeout = strtoul(cp, &cp, 10); + while (isspace(*cp)) + cp++; + if (!isdigit(*cp)) + goto inv; + cfg_retrans_count = strtoul(cp, &cp, 10); + while (isspace(*cp)) + cp++; + if (*cp != '\0' && *cp != '#') + goto inv; +} + +static void +process_line(st, line) + struct parse_state *st; + char *line; +{ + char *cp, *kw; + void (*handler)(), *var; + int set_id; + + if (!index(line, '\n')) { + fprintf(stderr, "%s line %d: too long or missing newline\n", + config_file_pathname, st->lineno); + exit(1); + } + for (cp = line; isspace(*cp); cp++) + ; + if (*cp == '\0' || *cp == '#') + return; + for (kw = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + if (!strcmp(kw, "bind-ip")) { + handler = handle_ip; + var = &sip_bind_ip; + set_id = 1; + } else if (!strcmp(kw, "bind-port")) { + handler = handle_num; + var = &sip_bind_port; + set_id = 2; + } else if (!strcmp(kw, "sip-udp-retrans")) { + handler = handle_retrans_conf; + var = (void *) 0; + set_id = 0; + } else if (!strcmp(kw, "sip-linger-timeout")) { + handler = handle_num; + var = &sip_linger_timeout; + set_id = 0; + } else if (!strcmp(kw, "sip-linger-invite-error")) { + handler = handle_num; + var = &sip_linger_invite_err; + set_id = 0; + } else if (!strcmp(kw, "sip-linger-got-bye")) { + handler = handle_num; + var = &sip_linger_gotbye; + set_id = 0; + } else if (!strcmp(kw, "sip-linger-bye-out-ok")) { + handler = handle_num; + var = &sip_linger_bye_out_ok; + set_id = 0; + } else if (!strcmp(kw, "sip-linger-bye-out-error")) { + handler = handle_num; + var = &sip_linger_bye_out_err; + set_id = 0; + } else if (!strcmp(kw, "max-forwards")) { + handler = &handle_num; + var = &max_forwards; + set_id = 0; + } else if (!strcmp(kw, "block-1900")) { + handler = handle_bool; + var = &block_1900_numbers; + set_id = 0; + } else { + fprintf(stderr, "%s line %d: non-understood keyword \"%s\"\n", + config_file_pathname, st->lineno, kw); + exit(1); + } + if (st->set_mask & set_id) { + fprintf(stderr, "%s line %d: duplicate %s setting\n", + config_file_pathname, st->lineno, kw); + exit(1); + } + while (isspace(*cp)) + cp++; + handler(st, kw, cp, var); + st->set_mask |= set_id; +} + +read_config_file() +{ + FILE *inf; + struct parse_state pst; + char linebuf[256]; + + inf = fopen(config_file_pathname, "r"); + if (!inf) { + perror(config_file_pathname); + exit(1); + } + pst.set_mask = 0; + for (pst.lineno = 1; fgets(linebuf, sizeof linebuf, inf); pst.lineno++) + process_line(&pst, linebuf); + fclose(inf); + if (pst.set_mask != 3) { + fprintf(stderr, "error: %s did not set all required settings\n", + config_file_pathname); + exit(1); + } +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/retrans.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/retrans.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,74 @@ +/* + * In this module we handle retransmission of our outgoing SIP UAC packets. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/gsm48_const.h" +#include "../include/out_routes.h" +#include "../libsip/out_msg.h" +#include "call.h" + +extern unsigned cfg_retrans_count; +extern unsigned sip_linger_timeout; +extern unsigned sip_linger_bye_out_err; +extern struct call *call_list; + +void +run_periodic_retrans() +{ + struct call *call; + struct sip_msg_out msg; + + for (call = call_list; call; call = call->next) { + switch (call->sip_state) { + case SIP_STATE_INV_SENT: + if (call->sip_tx_count < cfg_retrans_count) { + fill_invite_req_msg(&msg, call); + sip_tx_packet(&msg, &call->udp_sin); + call->sip_tx_count++; + } else { + syslog(LOG_ERR, + "outbound INVITE retrans timeout"); + call->overall_state = OVERALL_STATE_TEARDOWN; + disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_DEST_OOO); + disconnect_tmgw(call); + call->sip_state = SIP_STATE_ACCEPT_100; + sip_mark_end_time(call, sip_linger_timeout); + } + break; + case SIP_STATE_CANCEL_SENT: + if (call->sip_tx_count < cfg_retrans_count) { + fill_cancel_req_msg(&msg, call); + sip_tx_packet(&msg, &call->udp_sin); + call->sip_tx_count++; + } else { + syslog(LOG_ERR, + "outbound CANCEL retrans timeout"); + call->sip_state = SIP_STATE_ACCEPT_200; + sip_mark_end_time(call, sip_linger_timeout); + } + break; + case SIP_STATE_BYE_SENT: + if (call->sip_tx_count < cfg_retrans_count) { + fill_bye_req_msg(&msg, call); + sip_tx_packet(&msg, &call->udp_sin); + call->sip_tx_count++; + } else { + syslog(LOG_ERR, "outbound BYE retrans timeout"); + call->sip_state = SIP_STATE_ENDED; + sip_mark_end_time(call, sip_linger_bye_out_err); + } + break; + } + } +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/shutdown.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/shutdown.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,63 @@ +/* + * In this module we handle the scenarios of themwi-mncc and/or themwi-mgw + * shutting down while we are connected to them. In both scenarios we + * terminate all active calls (in the case of MNCC socket closing, only + * those calls that came on that socket), but our themwi-sip-out process + * itself stays running. This way once the other required processes restart, + * outbound calls will start working once again, without needing to restart + * themwi-sip-out. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/mncc.h" +#include "../include/gsm48_const.h" +#include "../include/out_routes.h" +#include "call.h" + +extern struct call *call_list; + +void +shutdown_mncc_socket(mncc) + struct mncc_conn *mncc; +{ + struct call *call; + + for (call = call_list; call; call = call->next) { + if (call->mncc != mncc) + continue; + call->mncc = 0; + if (call->overall_state < OVERALL_STATE_TEARDOWN) { + call->overall_state = OVERALL_STATE_TEARDOWN; + disconnect_tmgw(call); + disconnect_sip(call); + check_dead_call(call); + } + } +} + +void +shutdown_mgw_conn() +{ + struct call *call; + + for (call = call_list; call; call = call->next) { + call->mgw_state = MGW_STATE_NO_EXIST; + call->mgw_xact = 0; + if (call->overall_state < OVERALL_STATE_TEARDOWN) { + call->overall_state = OVERALL_STATE_TEARDOWN; + disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_NETWORK_OOO); + disconnect_sip(call); + check_dead_call(call); + } + } +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/sip_log.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/sip_log.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,68 @@ +/* + * In this module we implement debug logging of SIP Rx & Tx messages. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct timeval cur_event_time; + +static FILE *logfile; + +open_sip_log_file(filename) + char *filename; +{ + logfile = fopen(filename, "a"); + if (!logfile) { + perror(filename); + return(-1); + } + return(0); +} + +static void +log_common(msg, msglen, sin, dir) + char *msg, *dir; + unsigned msglen; + struct sockaddr_in *sin; +{ + unsigned sec, ms; + + sec = cur_event_time.tv_sec % 86400; + ms = cur_event_time.tv_usec / 1000; + fprintf(logfile, "Msg %s %s:%u %u bytes %02u:%02u:%02u.%03u\n", dir, + inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), msglen, + sec / 3600, (sec / 60) % 60, sec % 60, ms); + fwrite(msg, 1, msglen, logfile); + putc('\n', logfile); + fflush(logfile); +} + +void +log_sip_msg_rx(msg, msglen, sin) + char *msg; + unsigned msglen; + struct sockaddr_in *sin; +{ + if (!logfile) + return; + log_common(msg, msglen, sin, "from"); +} + +void +log_sip_msg_tx(msg, msglen, sin) + char *msg; + unsigned msglen; + struct sockaddr_in *sin; +{ + if (!logfile) + return; + log_common(msg, msglen, sin, "to"); +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/sip_uas.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/sip_uas.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,62 @@ +/* + * Basic UAS functions for themwi-sip-out. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../libsip/parse.h" +#include "../libsip/uas_basic.h" +#include "../libsip/out_msg.h" + +static void +unsupported_method(req, ess, sin) + struct sip_pkt_rx *req; + struct uas_parse_hdrs *ess; + struct sockaddr_in *sin; +{ + struct sip_msg_out resp; + int rc; + + start_response_out_msg(&resp, "501 Method not supported"); + rc = add_resp_basic_headers(&resp, ess, req->req_method); + if (rc < 0) { +too_long: syslog(LOG_ERR, "sending 501 error: response length exceeded"); + return; + } + rc = out_msg_add_header(&resp, "Allow", "INVITE,ACK,BYE"); + if (rc < 0) + goto too_long; + out_msg_finish(&resp); + sip_tx_packet(&resp, sin); +} + +void +process_sip_request(msg, sin) + struct sip_pkt_rx *msg; + struct sockaddr_in *sin; +{ + struct uas_parse_hdrs ess; + int rc; + + rc = uas_get_basic_headers(msg, &ess); + if (rc < 0) { + syslog(LOG_ERR, "SIP %.16s request: bad or missing %s header", + msg->req_method, ess.error_field); + return; + } + /* dispatch by method */ + if (!strcmp(msg->req_method, "INVITE")) + handle_invite_req(msg, &ess, sin); + else if (!strcmp(msg->req_method, "ACK")) + return; /* no handling required */ + else if (!strcmp(msg->req_method, "BYE")) + handle_bye_req(msg, &ess, sin); + else + unsupported_method(msg, &ess, sin); +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/sip_udp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/sip_udp.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,91 @@ +/* + * In this module we implement our UDP socket for SIP, + * and the associated lowest-level protocol handling. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../libsip/parse.h" +#include "../libsip/out_msg.h" + +extern struct in_addr sip_bind_ip; +extern unsigned sip_bind_port; + +int sip_socket; + +open_sip_udp_socket() +{ + struct sockaddr_in sin; + int rc; + + sip_socket = socket(AF_INET, SOCK_DGRAM, 0); + if (sip_socket < 0) { + syslog(LOG_CRIT, "socket(AF_INET, SOCK_DGRAM, 0): %m"); + return(-1); + } + sin.sin_family = AF_INET; + sin.sin_addr = sip_bind_ip; + sin.sin_port = htons(sip_bind_port); + rc = bind(sip_socket, (struct sockaddr *) &sin, sizeof sin); + if (rc < 0) { + syslog(LOG_CRIT, "bind of SIP UDP socket: %m"); + return(-1); + } + update_max_fd(sip_socket); + return(0); +} + +void +sip_socket_select() +{ + struct sip_pkt_rx pkt; + struct sockaddr_in sin; + socklen_t addrlen; + int rc; + + addrlen = sizeof sin; + rc = recvfrom(sip_socket, pkt.pkt_buffer, MAX_SIP_RX_PACKET, 0, + (struct sockaddr *) &sin, &addrlen); + if (rc <= 0) + return; + pkt.pkt_length = rc; + log_sip_msg_rx(pkt.pkt_buffer, pkt.pkt_length, &sin); + rc = parse_incoming_sip_msg(&pkt); + if (rc < 0) { + /* parse errors */ + if (rc == -2 && pkt.parse_msgtype == SIP_MSG_TYPE_REQ) + syslog(LOG_ERR, + "SIP %.16s msg exceeds MAX_HEADER_FIELDS", + pkt.req_method); + else if (rc == -2 && pkt.parse_msgtype == SIP_MSG_TYPE_RESP) + syslog(LOG_ERR, + "SIP response msg exceeds MAX_HEADER_FIELDS"); + /* in any case, silently discard */ + return; + } + /* dispatch good-so-far SIP message */ + if (pkt.parse_msgtype == SIP_MSG_TYPE_REQ) + process_sip_request(&pkt, &sin); + else if (pkt.parse_msgtype == SIP_MSG_TYPE_RESP) + process_sip_response(&pkt, &sin); +} + +void +sip_tx_packet(msg, sin) + struct sip_msg_out *msg; + struct sockaddr_in *sin; +{ + socklen_t addrlen; + + addrlen = sizeof(struct sockaddr_in); + sendto(sip_socket, msg->buf, msg->msg_len, 0, (struct sockaddr *) sin, + addrlen); + log_sip_msg_tx(msg->buf, msg->msg_len, sin); +} diff -r 99fd4ae573ae -r e54b0a9e322f sip-out/uac_out.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-out/uac_out.c Tue Oct 11 23:04:01 2022 -0800 @@ -0,0 +1,190 @@ +/* + * In this module we implement generation of outgoing SIP messages + * in the UAC role. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/gsm48_const.h" +#include "../include/out_routes.h" +#include "../libsip/out_msg.h" +#include "../libsip/sdp.h" +#include "call.h" + +extern struct call *find_call_by_sip_id(); + +extern struct in_addr sip_bind_ip; +extern unsigned sip_bind_port; +extern unsigned max_forwards; +extern struct timeval cur_event_time; + +add_req_boilerplate(msg, call, cseq, to_tag) + struct sip_msg_out *msg; + struct call *call; + char *cseq, *to_tag; +{ + char strbuf[256]; + int rc; + + sprintf(strbuf, "SIP/2.0/UDP %s:%u", + inet_ntoa(sip_bind_ip), sip_bind_port); + rc = out_msg_add_header(msg, "Via", strbuf); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "From", call->from_header); + if (rc < 0) + return rc; + if (to_tag && to_tag[0]) { + sprintf(strbuf, "<%s>;tag=%s", call->to_uri, to_tag); + rc = out_msg_add_header(msg, "To", strbuf); + } else + rc = out_msg_add_header(msg, "To", call->to_uri); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "Call-ID", call->sip_call_id); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "CSeq", cseq); + if (rc < 0) + return rc; + sprintf(strbuf, "%u", max_forwards); + return out_msg_add_header(msg, "Max-Forwards", strbuf); +} + +add_contact_header(msg) + struct sip_msg_out *msg; +{ + char strbuf[80]; + + sprintf(strbuf, "", + inet_ntoa(sip_bind_ip), sip_bind_port); + return out_msg_add_header(msg, "Contact", strbuf); +} + +fill_invite_req_msg(msg, call) + struct sip_msg_out *msg; + struct call *call; +{ + struct sdp_gen sdp; + int rc; + + rc = start_request_out_msg(msg, "INVITE", call->to_uri); + if (rc < 0) + return rc; + rc = add_req_boilerplate(msg, call, "1 INVITE", (char *) 0); + if (rc < 0) + return rc; + rc = add_contact_header(msg); + 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 = SDP_CODEC_MASK_BOTH; + sdp.session_id = sdp.conn_port << 16; + sdp.owner_ip = sip_bind_ip; + return out_msg_finish_sdp(msg, &sdp); +} + +fill_cancel_req_msg(msg, call) + struct sip_msg_out *msg; + struct call *call; +{ + int rc; + + rc = start_request_out_msg(msg, "CANCEL", call->to_uri); + if (rc < 0) + return rc; + rc = add_req_boilerplate(msg, call, "1 CANCEL", (char *) 0); + if (rc < 0) + return rc; + out_msg_finish(msg); + return 0; +} + +fill_bye_req_msg(msg, call) + struct sip_msg_out *msg; + struct call *call; +{ + int rc; + + rc = start_request_out_msg(msg, "BYE", call->to_uri); + if (rc < 0) + return rc; + rc = add_req_boilerplate(msg, call, "2 BYE", call->to_tag); + if (rc < 0) + return rc; + out_msg_finish(msg); + return 0; +} + +void +start_sip_out(call) + struct call *call; +{ + struct sip_msg_out msg; + int rc; + + sprintf(call->sip_call_id, "%08u_%u@%s", + (unsigned)(cur_event_time.tv_sec % 100000000), + ntohs(call->pstn_rtp_local.sin_port), inet_ntoa(sip_bind_ip)); + if (find_call_by_sip_id(call->sip_call_id)) { + syslog(LOG_ERR, "generated Call-ID collision!"); + call->overall_state = OVERALL_STATE_TEARDOWN; + disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SWITCH_CONG); + disconnect_tmgw(call); + return; + } + sprintf(call->from_header, ";tag=out%u", call->from_user, + inet_ntoa(sip_bind_ip), ntohs(call->pstn_rtp_local.sin_port)); + rc = fill_invite_req_msg(&msg, call); + if (rc < 0) { + syslog(LOG_CRIT, "generated SIP INVITE req exceeds msg size!"); + call->overall_state = OVERALL_STATE_TEARDOWN; + disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_NETWORK_OOO); + disconnect_tmgw(call); + return; + } + sip_tx_packet(&msg, &call->udp_sin); + call->overall_state = OVERALL_STATE_SIP_OUT; + call->sip_call_exists = 1; + call->sip_state = SIP_STATE_INV_SENT; + call->sip_tx_count = 1; +} + +void +initiate_sip_cancel(call) + struct call *call; +{ + struct sip_msg_out msg; + + fill_cancel_req_msg(&msg, call); + sip_tx_packet(&msg, &call->udp_sin); + call->sip_state = SIP_STATE_CANCEL_SENT; + call->sip_tx_count = 1; +} + +void +initiate_bye(call) + struct call *call; +{ + struct sip_msg_out msg; + + fill_bye_req_msg(&msg, call); + sip_tx_packet(&msg, &call->udp_sin); + call->sip_state = SIP_STATE_BYE_SENT; + call->sip_tx_count = 1; +}