Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package osmo-sip-connector for openSUSE:Factory checked in at 2021-10-15 23:03:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/osmo-sip-connector (Old) and /work/SRC/openSUSE:Factory/.osmo-sip-connector.new.1890 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "osmo-sip-connector" Fri Oct 15 23:03:38 2021 rev:3 rq:925170 version:1.4.0 Changes: -------- --- /work/SRC/openSUSE:Factory/osmo-sip-connector/osmo-sip-connector.changes 2020-01-09 22:50:08.254695560 +0100 +++ /work/SRC/openSUSE:Factory/.osmo-sip-connector.new.1890/osmo-sip-connector.changes 2021-10-15 23:03:53.942094701 +0200 @@ -1,0 +2,6 @@ +Wed Oct 13 13:55:55 UTC 2021 - Johannes Segitz <jseg...@suse.com> + +- Added hardening to systemd service(s) (bsc#1181400). Added patch(es): + * harden_osmo-sip-connector.service.patch + +------------------------------------------------------------------- Old: ---- osmo-sip-connector-1.2.0.tar.xz New: ---- harden_osmo-sip-connector.service.patch osmo-sip-connector-1.4.0.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ osmo-sip-connector.spec ++++++ --- /var/tmp/diff_new_pack.4IbOhy/_old 2021-10-15 23:03:54.374095010 +0200 +++ /var/tmp/diff_new_pack.4IbOhy/_new 2021-10-15 23:03:54.374095010 +0200 @@ -1,8 +1,8 @@ # # spec file for package osmo-sip-connector # -# Copyright (c) 2020 SUSE LINUX GmbH, Nuernberg, Germany. -# Copyright (c) 2016, Martin Hauke <mar...@gmx.de> +# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2016-2020, Martin Hauke <mar...@gmx.de> # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,13 +18,14 @@ Name: osmo-sip-connector -Version: 1.2.0 +Version: 1.4.0 Release: 0 Summary: MNCC to SIP bridge for osmo-nitb License: AGPL-3.0-or-later AND GPL-2.0-or-later Group: Productivity/Telephony/Servers URL: http://openbsc.osmocom.org/ Source: %{name}-%{version}.tar.xz +Patch0: harden_osmo-sip-connector.service.patch BuildRequires: autoconf BuildRequires: automake BuildRequires: libtool @@ -41,6 +42,7 @@ %prep %setup -q +%patch0 -p1 %build echo "%{version}" >.tarball-version ++++++ harden_osmo-sip-connector.service.patch ++++++ Index: osmo-sip-connector-1.4.0/contrib/systemd/osmo-sip-connector.service =================================================================== --- osmo-sip-connector-1.4.0.orig/contrib/systemd/osmo-sip-connector.service +++ osmo-sip-connector-1.4.0/contrib/systemd/osmo-sip-connector.service @@ -4,6 +4,17 @@ After=network-online.target Wants=network-online.target [Service] +# added automatically, for details please see +# https://en.opensuse.org/openSUSE:Security_Features#Systemd_hardening_effort +ProtectSystem=full +ProtectHome=true +ProtectHostname=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectKernelLogs=true +ProtectControlGroups=true +RestrictRealtime=true +# end of automatic additions Type=simple Restart=always ExecStart=/usr/bin/osmo-sip-connector -c /etc/osmocom/osmo-sip-connector.cfg ++++++ osmo-sip-connector-1.2.0.tar.xz -> osmo-sip-connector-1.4.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/configure.ac new/osmo-sip-connector-1.4.0/configure.ac --- old/osmo-sip-connector-1.2.0/configure.ac 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/configure.ac 2020-01-02 21:35:01.000000000 +0100 @@ -22,9 +22,9 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_PROG_CC -PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0) -PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.11.0) -PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0) +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.0) +PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0) +PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0) PKG_CHECK_MODULES(SOFIASIP, sofia-sip-ua-glib >= 1.12.0) AC_ARG_ENABLE(sanitize, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/contrib/jenkins.sh new/osmo-sip-connector-1.4.0/contrib/jenkins.sh --- old/osmo-sip-connector-1.2.0/contrib/jenkins.sh 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/contrib/jenkins.sh 2020-01-02 21:35:01.000000000 +0100 @@ -52,4 +52,5 @@ make -C "$base/doc/manuals" publish fi +$MAKE maintainer-clean osmo-clean-workspace.sh diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/contrib/systemd/osmo-sip-connector.service new/osmo-sip-connector-1.4.0/contrib/systemd/osmo-sip-connector.service --- old/osmo-sip-connector-1.2.0/contrib/systemd/osmo-sip-connector.service 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/contrib/systemd/osmo-sip-connector.service 2020-01-02 21:35:01.000000000 +0100 @@ -1,5 +1,7 @@ [Unit] Description=Osmo SIP Connector +After=network-online.target +Wants=network-online.target [Service] Type=simple diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/debian/changelog new/osmo-sip-connector-1.4.0/debian/changelog --- old/osmo-sip-connector-1.2.0/debian/changelog 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/debian/changelog 2020-01-02 21:35:01.000000000 +0100 @@ -1,3 +1,66 @@ +osmo-sip-connector (1.4.0) unstable; urgency=medium + + [ Keith ] + * coverity: Address issue found by coverity + + [ Philipp Maier ] + * mncc: check fd before closing a connection + + [ Alexander Couzens ] + * .service: require to ran after network-online.target + * exit if the sip agent failed to start + + [ Oliver Smith ] + * mncc.c: fix gsm_mncc_rtp size checks + * osmoappdesc.py: switch to python 3 + + [ Neels Hofmeyr ] + * MNCC v6: add optional SDP to the socket protocol + * logging from sofia: add missing newline + + -- Pau Espin Pedrol <pes...@sysmocom.de> Thu, 02 Jan 2020 21:35:01 +0100 + +osmo-sip-connector (1.3.0) unstable; urgency=medium + + [ Keith ] + * Support International Caller ID + * Support Emergency Calling + * Add media mode parameter to sdp_create_file() + * Handle SIP re-INVITEs + * Prepare to support MO Call HOLD + * Add support for MNCC HOLD/RETRIEVE + * Only send update to MNCC if media info changed. + * IPs stored in the call struct are NETWORK byte order + * Following from previous commit, assume other->ip is network byte order + * inet_ntoa() is deprecated, use inet_ntop() instead + * Fix bug with MO calls not connecting. + * Define hold/retrieve functions for MO calls + * Cosmetic: Logging and Comments, spelling and clarity changes + * Handle case where SIP side hangsup while MNCC is on hold + * Logging: Make use of Levels, clarify some messages + * In hold/retrieve/reinvite, add check that other_leg exists + + [ Harald Welte ] + * cosmetic: use OSMO_STRLCPY_ARRAY() instead of osmo_strlcpy() + * MNCC: Do not continue with B leg if A leg is cancelled. + + [ Rafael Diniz ] + * Added daemonize feature to osmo-sip-connector + + [ Daniel Willmann ] + * Properly indent config file + + [ Oliver Smith ] + * Cosmetic: debian: build-depends in multiple lines + * debian: create -doc subpackage with pdf manuals + * contrib/jenkins.sh: run "make maintainer-clean" + + [ Pau Espin Pedrol ] + * Remove undefined param passed to {logging,osmo_stats}_vty_add_cmds + * Require newer libosmocore 1.0.0 + + -- Pau Espin Pedrol <pes...@sysmocom.de> Thu, 08 Aug 2019 17:54:49 +0200 + osmo-sip-connector (1.2.0) unstable; urgency=medium [ Keith ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/debian/control new/osmo-sip-connector-1.4.0/debian/control --- old/osmo-sip-connector-1.2.0/debian/control 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/debian/control 2020-01-02 21:35:01.000000000 +0100 @@ -2,7 +2,15 @@ Section: net Priority: optional Maintainer: Holger Hans Peter Freyther <hol...@moiji-mobile.com> -Build-Depends: debhelper (>= 7.0.0~), autotools-dev, pkg-config, libosmocore-dev, libsofia-sip-ua-glib-dev, libsofia-sip-ua-dev, dh-systemd (>= 1.5), dh-autoreconf +Build-Depends: debhelper (>= 7.0.0~), + autotools-dev, + pkg-config, + libsofia-sip-ua-glib-dev, + libsofia-sip-ua-dev, + dh-systemd (>= 1.5), + dh-autoreconf, + libosmocore-dev (>= 1.0.0), + osmo-gsm-manuals-dev Standards-Version: 3.8.4 Homepage: https://osmocom.org/projects/osmo-sip-conector/wiki Vcs-Git: git://git.osmocom.org/osmo-sip-connector.git @@ -21,3 +29,12 @@ Depends: osmo-sip-connector (= ${binary:Version}), ${misc:Depends} Description: Debug symbols for the osmo-sip-connector Make debugging possible + +Package: osmo-sip-connector-doc +Architecture: all +Section: doc +Priority: optional +Depends: ${misc:Depends} +Description: ${misc:Package} PDF documentation + Various manuals: user manual, VTY reference manual and/or + protocol/interface manuals. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/debian/osmo-sip-connector-doc.install new/osmo-sip-connector-1.4.0/debian/osmo-sip-connector-doc.install --- old/osmo-sip-connector-1.2.0/debian/osmo-sip-connector-doc.install 1970-01-01 01:00:00.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/debian/osmo-sip-connector-doc.install 2020-01-02 21:35:01.000000000 +0100 @@ -0,0 +1 @@ +usr/share/doc/osmo-sip-connector-doc/*.pdf diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/debian/rules new/osmo-sip-connector-1.4.0/debian/rules --- old/osmo-sip-connector-1.2.0/debian/rules 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/debian/rules 2020-01-02 21:35:01.000000000 +0100 @@ -15,4 +15,8 @@ dh_strip --dbg-package=osmo-sip-connector-dbg override_dh_auto_configure: - dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system + dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals + +# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers) +override_dh_compress: + dh_compress -X.pdf diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/doc/examples/osmo-sip-connector.cfg new/osmo-sip-connector-1.4.0/doc/examples/osmo-sip-connector.cfg --- old/osmo-sip-connector-1.2.0/doc/examples/osmo-sip-connector.cfg 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/doc/examples/osmo-sip-connector.cfg 2020-01-02 21:35:01.000000000 +0100 @@ -1,6 +1,6 @@ app mncc - socket-path /tmp/msc_mncc + socket-path /tmp/msc_mncc sip - local 0.0.0.0 5060 - remote pbx 5060 + local 0.0.0.0 5060 + remote pbx 5060 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/doc/manuals/Makefile.am new/osmo-sip-connector-1.4.0/doc/manuals/Makefile.am --- old/osmo-sip-connector-1.2.0/doc/manuals/Makefile.am 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/doc/manuals/Makefile.am 2020-01-02 21:35:01.000000000 +0100 @@ -12,5 +12,6 @@ VTY_REFERENCE = osmosipconnector-vty-reference.xml include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc + OSMO_REPOSITORY = osmo-sip-connector include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/osmoappdesc.py new/osmo-sip-connector-1.4.0/osmoappdesc.py --- old/osmo-sip-connector-1.2.0/osmoappdesc.py 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/osmoappdesc.py 2020-01-02 21:35:01.000000000 +0100 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # (C) 2016 by Holger Hans Peter Freyther diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/src/call.c new/osmo-sip-connector-1.4.0/src/call.c --- old/osmo-sip-connector-1.2.0/src/call.c 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/src/call.c 2020-01-02 21:35:01.000000000 +0100 @@ -40,6 +40,7 @@ { MNCC_CC_INITIAL, "INITIAL" }, { MNCC_CC_PROCEEDING, "PROCEEDING" }, { MNCC_CC_CONNECTED, "CONNECTED" }, + { MNCC_CC_HOLD, "ON HOLD" }, { 0, NULL }, }; @@ -53,6 +54,7 @@ { SIP_CC_INITIAL, "INITIAL" }, { SIP_CC_DLG_CNFD, "CONFIRMED" }, { SIP_CC_CONNECTED, "CONNECTED" }, + { SIP_CC_HOLD, "ON HOLD" }, { 0, NULL }, }; @@ -167,6 +169,6 @@ mncc = (struct mncc_call_leg *) leg; return get_value_string(mncc_state_vals, mncc->state); default: - return "unknown call type"; + return "Unknown call type"; } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/src/call.h new/osmo-sip-connector-1.4.0/src/call.h --- old/osmo-sip-connector-1.2.0/src/call.h 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/src/call.h 2020-01-02 21:35:01.000000000 +0100 @@ -54,6 +54,11 @@ uint32_t payload_type; uint32_t payload_msg_type; + /* SDP as received for this call leg. If this is an MNCC call leg, contains the SDP most recently received in an + * MNCC message; if this is a SIP call leg, contains the SDP most recently received in a SIP message. If no SDP + * was received yet, this string is empty. Otherwise a nul terminated string. */ + char sdp[1024]; + /** * Remote started to ring/alert */ @@ -74,12 +79,27 @@ * A DTMF key was entered. Forward it. */ void (*dtmf)(struct call_leg *, int keypad); + + /** + * Call HOLD requested + */ + void (*hold_call)(struct call_leg *); + + /** + * Call HOLD ended + */ + void (*retrieve_call)(struct call_leg *); + + + void (*update_rtp)(struct call_leg *); + }; enum sip_cc_state { SIP_CC_INITIAL, SIP_CC_DLG_CNFD, SIP_CC_CONNECTED, + SIP_CC_HOLD, }; enum sip_dir { @@ -110,6 +130,7 @@ MNCC_CC_INITIAL, MNCC_CC_PROCEEDING, /* skip delivered state */ MNCC_CC_CONNECTED, + MNCC_CC_HOLD, }; enum mncc_dir { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/src/main.c new/osmo-sip-connector-1.4.0/src/main.c --- old/osmo-sip-connector-1.2.0/src/main.c 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/src/main.c 2020-01-02 21:35:01.000000000 +0100 @@ -46,6 +46,7 @@ void *tall_mncc_ctx; +static bool daemonize = false; static char *config_file = "osmo-sip-connector.cfg"; static struct log_info_cat mncc_sip_categories[] = { @@ -79,8 +80,9 @@ static void print_help(void) { printf("OsmoSIPcon: MNCC to SIP bridge\n"); - printf(" -h --help\tthis text\n"); + printf(" -h --help\tThis text\n"); printf(" -c --config-file NAME\tThe config file to use [%s]\n", config_file); + printf(" -D --daemonize\tFork the process into a background daemon\n"); printf(" -V --version\tPrint the version number\n"); } @@ -91,11 +93,12 @@ static struct option long_options[] = { {"help", 0, 0, 'h'}, {"config-file", 1, 0, 'c'}, + {"daemonize", 0, 0, 'D'}, {"version", 0, 0, 'V' }, {NULL, 0, 0, 0} }; - c = getopt_long(argc, argv, "hc:V", + c = getopt_long(argc, argv, "hc:DV", long_options, &option_index); if (c == -1) break; @@ -107,6 +110,9 @@ case 'c': config_file = optarg; break; + case 'D': + daemonize = true; + break; case 'V': print_version(1); exit(EXIT_SUCCESS); @@ -127,8 +133,8 @@ osmo_stats_init(tall_mncc_ctx); mncc_sip_vty_init(); - logging_vty_add_cmds(&mncc_sip_info); - osmo_stats_vty_add_cmds(&mncc_sip_info); + logging_vty_add_cmds(); + osmo_stats_vty_add_cmds(); /* parsing and setup */ @@ -152,13 +158,23 @@ /* sofia sip */ sip_agent_init(&g_app.sip.agent, &g_app); rc = sip_agent_start(&g_app.sip.agent); - if (rc < 0) + if (rc < 0) { LOGP(DSIP, LOGL_ERROR, - "Failed to initialize SIP. Running broken\n"); + "Failed to initialize SIP\n"); + exit(1); + } calls_init(); app_setup(&g_app); + if (daemonize) { + rc = osmo_daemonize(); + if (rc < 0) { + perror("Error during daemonize"); + exit(1); + } + } + /* marry sofia-sip to glib and glib to libosmocore */ loop = g_main_loop_new(NULL, FALSE); g_source_attach(su_glib_root_gsource(g_app.sip.agent.root), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/src/mncc.c new/osmo-sip-connector-1.4.0/src/mncc.c --- old/osmo-sip-connector-1.2.0/src/mncc.c 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/src/mncc.c 2020-01-02 21:35:01.000000000 +0100 @@ -50,7 +50,7 @@ struct mncc_call_leg *leg = data; struct call_leg *other_leg; - LOGP(DMNCC, LOGL_ERROR, "cmd(0x%x) never arrived for leg(%u)\n", + LOGP(DMNCC, LOGL_ERROR, "command(0x%x) never arrived for leg(%u)\n", leg->rsp_wanted, leg->callref); other_leg = call_leg_other(&leg->base); @@ -72,7 +72,7 @@ static void stop_cmd_timer(struct mncc_call_leg *leg, uint32_t got_res) { if (leg->rsp_wanted != got_res) { - LOGP(DMNCC, LOGL_ERROR, "Wanted rsp(%s) but got(%s) for leg(%u)\n", + LOGP(DMNCC, LOGL_ERROR, "Wanted response(%s) but got(%s) for leg(%u)\n", osmo_mncc_name(leg->rsp_wanted), osmo_mncc_name(got_res), leg->callref); return; } @@ -104,6 +104,17 @@ return NULL; } +/* Find a MNCC Call leg (by callref) which is not yet in release */ +static struct mncc_call_leg *mncc_find_leg_not_released(uint32_t callref) +{ + struct mncc_call_leg *leg = mncc_find_leg(callref); + if (!leg) + return NULL; + if (leg->base.in_release) + return NULL; + return leg; +} + static void mncc_fill_header(struct gsm_mncc *mncc, uint32_t msg_type, uint32_t callref) { struct mncc_call_leg *mncc_leg; @@ -130,7 +141,7 @@ rc = write(conn->fd.fd, mncc, sizeof(*mncc)); LOGP(DMNCC, LOGL_DEBUG, "MNCC sent message type: %s\n", osmo_mncc_name(mncc->msg_type)); if (rc != sizeof(*mncc)) { - LOGP(DMNCC, LOGL_ERROR, "Failed to send message call(%u)\n", callref); + LOGP(DMNCC, LOGL_ERROR, "Failed to send message for call(%u)\n", callref); close_connection(conn); } } @@ -153,16 +164,17 @@ rc = write(conn->fd.fd, &mncc, sizeof(mncc)); if (rc != sizeof(mncc)) { - LOGP(DMNCC, LOGL_ERROR, "Failed to send message call(%u)\n", callref); + LOGP(DMNCC, LOGL_ERROR, "Failed to send message for call(%u)\n", callref); close_connection(conn); } } -/* Send a MNCC_RTP_CONNET to the MSC for the given call legs */ +/* Send a MNCC_RTP_CONNECT to the MSC for the given call legs */ static bool send_rtp_connect(struct mncc_call_leg *leg, struct call_leg *other) { struct gsm_mncc_rtp mncc = { 0, }; int rc; + char ip_addr[INET_ADDRSTRLEN]; /* * Send RTP CONNECT and we handle the general failure of it by @@ -170,16 +182,19 @@ */ mncc.msg_type = MNCC_RTP_CONNECT; mncc.callref = leg->callref; - mncc.ip = htonl(other->ip); + mncc.ip = ntohl(other->ip); mncc.port = other->port; mncc.payload_type = other->payload_type; /* * FIXME: mncc.payload_msg_type should already be compatible.. but * payload_type should be different.. */ + struct in_addr net = { .s_addr = other->ip }; + inet_ntop(AF_INET, &net, ip_addr, sizeof(ip_addr)); + LOGP(DMNCC, LOGL_DEBUG, "SEND rtp_connect: IP=(%s) PORT=(%u)\n", ip_addr, mncc.port); rc = write(leg->conn->fd.fd, &mncc, sizeof(mncc)); if (rc != sizeof(mncc)) { - LOGP(DMNCC, LOGL_ERROR, "Failed to send message leg(%u)\n", + LOGP(DMNCC, LOGL_ERROR, "Failed to send message for call(%u)\n", leg->callref); close_connection(leg->conn); return false; @@ -187,6 +202,33 @@ return true; } +static void update_rtp(struct call_leg *_leg) { + + struct mncc_call_leg *leg; + + LOGP(DMNCC, LOGL_DEBUG, "UPDATE RTP for LEG Type (%u)\n", _leg->type); + + if (_leg->type == CALL_TYPE_MNCC) { + leg = (struct mncc_call_leg *) _leg; + struct call_leg *other = call_leg_other(&leg->base); + if (!other) + goto ret_release; + send_rtp_connect(leg, other); + } else if (_leg->type == CALL_TYPE_SIP) { + leg = (struct mncc_call_leg *) call_leg_other(_leg); + if (!leg) + goto ret_release; + send_rtp_connect(leg, _leg); + } else { + OSMO_ASSERT(false); + } + return; +ret_release: + LOGP(DMNCC, LOGL_ERROR, "Failed to find other leg.\n"); + _leg->release_call(_leg); +} + + /* CONNECT call-back for MNCC call leg */ static void mncc_call_leg_connect(struct call_leg *_leg) { @@ -219,7 +261,7 @@ mncc_fill_header(&out_mncc, MNCC_ALERT_REQ, leg->callref); /* GSM 04.08 10.5.4.21 */ out_mncc.fields |= MNCC_F_PROGRESS; - out_mncc.progress.coding = GSM48_CAUSE_CODING_GSM; /* Standard defined for the GSM??PLMNS */ + out_mncc.progress.coding = GSM48_CAUSE_CODING_GSM; /* Standard defined for the GSM PLMNS */ out_mncc.progress.location = GSM48_CAUSE_LOC_PRN_S_LU; /* Private network serving the local user */ out_mncc.progress.descr = GSM48_PROGR_IN_BAND_AVAIL; /* In-band information or appropriate pattern now available */ @@ -245,7 +287,7 @@ /* drop it directly, if not connected */ if (leg->conn->state != MNCC_READY) { LOGP(DMNCC, LOGL_DEBUG, - "MNCC not connected releasing leg leg(%u)\n", leg->callref); + "MNCC not connected releasing leg(%u)\n", leg->callref); return mncc_leg_release(leg); } @@ -265,6 +307,7 @@ break; case MNCC_CC_PROCEEDING: case MNCC_CC_CONNECTED: + case MNCC_CC_HOLD: LOGP(DMNCC, LOGL_DEBUG, "Releasing call in non-initial leg(%u) cause(%s)\n", leg->callref, gsm48_cc_cause_name(leg->base.cause)); leg->base.in_release = true; @@ -281,8 +324,12 @@ /* Close the MNCC connection/socket */ static void close_connection(struct mncc_connection *conn) { + if (conn->fd.fd < 0) + return; + osmo_fd_unregister(&conn->fd); close(conn->fd.fd); + conn->fd.fd = -1; osmo_timer_schedule(&conn->reconnect, 5, 0); conn->state = MNCC_DISCONNECTED; if (conn->on_disconnect) @@ -343,7 +390,7 @@ } rtp = (const struct gsm_mncc_rtp *) buf; - leg = mncc_find_leg(rtp->callref); + leg = mncc_find_leg_not_released(rtp->callref); if (!leg) { LOGP(DMNCC, LOGL_ERROR, "leg(%u) can not be found\n", rtp->callref); return mncc_send(conn, MNCC_REJ_REQ, rtp->callref); @@ -365,6 +412,7 @@ { const struct gsm_mncc_rtp *rtp; struct mncc_call_leg *leg; + char ip_addr[INET_ADDRSTRLEN]; if (rc < sizeof(*rtp)) { LOGP(DMNCC, LOGL_ERROR, "gsm_mncc_rtp of wrong size %d < %zu\n", @@ -373,23 +421,24 @@ } rtp = (const struct gsm_mncc_rtp *) buf; - leg = mncc_find_leg(rtp->callref); + leg = mncc_find_leg_not_released(rtp->callref); if (!leg) { LOGP(DMNCC, LOGL_ERROR, "call(%u) can not be found\n", rtp->callref); return mncc_send(conn, MNCC_REJ_REQ, rtp->callref); } /* extract information about where the RTP is */ - leg->base.ip = rtp->ip; + leg->base.ip = htonl(rtp->ip); leg->base.port = rtp->port; leg->base.payload_type = rtp->payload_type; leg->base.payload_msg_type = rtp->payload_msg_type; /* TODO.. now we can continue with the call */ - struct in_addr net = { .s_addr = htonl(leg->base.ip) }; - LOGP(DMNCC, LOGL_DEBUG, - "RTP cnt leg(%u) ip(%s), port(%u) pt(%u) ptm(%u)\n", - leg->callref, inet_ntoa(net), leg->base.port, + struct in_addr net = { .s_addr = leg->base.ip }; + inet_ntop(AF_INET, &net, ip_addr, sizeof(ip_addr)); + LOGP(DMNCC, LOGL_INFO, + "RTP continue leg(%u) ip(%s), port(%u) pt(%u) ptm(%u)\n", + leg->callref, ip_addr, leg->base.port, leg->base.payload_type, leg->base.payload_msg_type); stop_cmd_timer(leg, MNCC_RTP_CREATE); continue_call(leg); @@ -409,28 +458,42 @@ } } +static const struct gsm_mncc_number emergency_number = { + .type = CALL_TYPE_MNCC, + .plan = GSM48_NPI_UNKNOWN, + .number = "emergency", +}; + /* Check + Process MNCC_SETUP_IND (MO call) */ static void check_setup(struct mncc_connection *conn, const char *buf, int rc) { const struct gsm_mncc *data; + const struct gsm_mncc_number *called; struct call *call; struct mncc_call_leg *leg; - if (rc != sizeof(*data)) { + if (rc < sizeof(*data)) { LOGP(DMNCC, LOGL_ERROR, "gsm_mncc of wrong size %d vs. %zu\n", rc, sizeof(*data)); return close_connection(conn); } data = (const struct gsm_mncc *) buf; + called = &data->called; /* screen arguments */ if ((data->fields & MNCC_F_CALLED) == 0) { - LOGP(DMNCC, LOGL_ERROR, - "MNCC leg(%u) without called addr fields(%u)\n", - data->callref, data->fields); - return mncc_send(conn, MNCC_REJ_REQ, data->callref); + if (!data->emergency) { + LOGP(DMNCC, LOGL_ERROR, + "MNCC leg(%u) without called addr fields(%u)\n", + data->callref, data->fields); + return mncc_send(conn, MNCC_REJ_REQ, data->callref); + } + + /* Emergency without a called number present. Use the standard "emergency" number. */ + called = &emergency_number; } + if ((data->fields & MNCC_F_CALLING) == 0) { LOGP(DMNCC, LOGL_ERROR, "MNCC leg(%u) without calling addr fields(%u)\n", @@ -457,15 +520,16 @@ leg->base.connect_call = mncc_call_leg_connect; leg->base.ring_call = mncc_call_leg_ring; leg->base.release_call = mncc_call_leg_release; + leg->base.update_rtp = update_rtp; leg->callref = data->callref; leg->conn = conn; leg->state = MNCC_CC_INITIAL; leg->dir = MNCC_DIR_MO; - memcpy(&leg->called, &data->called, sizeof(leg->called)); + memcpy(&leg->called, called, sizeof(leg->called)); memcpy(&leg->calling, &data->calling, sizeof(leg->calling)); memcpy(&leg->imsi, data->imsi, sizeof(leg->imsi)); - LOGP(DMNCC, LOGL_DEBUG, + LOGP(DMNCC, LOGL_INFO, "Created call(%u) with MNCC leg(%u) IMSI(%.16s)\n", call->id, leg->callref, data->imsi); @@ -484,7 +548,7 @@ { struct mncc_call_leg *leg; - if (rc != sizeof(**mncc)) { + if (rc < sizeof(**mncc)) { LOGP(DMNCC, LOGL_ERROR, "gsm_mncc of wrong size %d vs. %zu\n", rc, sizeof(**mncc)); close_connection(conn); @@ -574,7 +638,7 @@ if (!leg) return; - LOGP(DMNCC, LOGL_NOTICE, "leg(%u) is now connected.\n", leg->callref); + LOGP(DMNCC, LOGL_INFO, "leg(%u) is now connected.\n", leg->callref); stop_cmd_timer(leg, MNCC_SETUP_COMPL_IND); leg->state = MNCC_CC_CONNECTED; } @@ -609,7 +673,7 @@ return; LOGP(DMNCC, LOGL_DEBUG, - "leg(%u) confirmend. creating RTP socket.\n", + "leg(%u) confirmed. creating RTP socket.\n", leg->callref); start_cmd_timer(leg, MNCC_RTP_CREATE); @@ -644,14 +708,54 @@ { const struct gsm_mncc *data; struct mncc_call_leg *leg; + struct call_leg *other_leg; leg = find_leg(conn, buf, rc, &data); if (!leg) return; LOGP(DMNCC, LOGL_DEBUG, - "leg(%u) is req hold. rejecting.\n", leg->callref); - mncc_send(leg->conn, MNCC_HOLD_REJ, leg->callref); + "leg(%u) is requesting hold.\n", leg->callref); + other_leg = call_leg_other(&leg->base); + if (!other_leg) { + LOGP(DMNCC, LOGL_ERROR, "leg(%u) other leg gone!\n", + leg->callref); + mncc_send(leg->conn, MNCC_HOLD_REJ, leg->callref); + return; + } + other_leg->hold_call(other_leg); + mncc_send(leg->conn, MNCC_HOLD_CNF, leg->callref); + leg->state = MNCC_CC_HOLD; +} + +static void check_retrieve_ind(struct mncc_connection *conn, const char *buf, int rc) +{ + const struct gsm_mncc *data; + struct mncc_call_leg *leg; + struct call_leg *other_leg; + + leg = find_leg(conn, buf, rc, &data); + if (!leg) + return; + + LOGP(DMNCC, LOGL_DEBUG, + "leg(%u) is requesting unhold.\n", leg->callref); + other_leg = call_leg_other(&leg->base); + if (!other_leg) { + /* The SIP leg went away while we were holding! */ + LOGP(DMNCC, LOGL_ERROR, "leg(%u) other leg gone!\n", + leg->callref); + mncc_send(leg->conn, MNCC_RETRIEVE_CNF, leg->callref); + mncc_call_leg_release(&leg->base); + return; + } + other_leg->retrieve_call(other_leg); + mncc_send(leg->conn, MNCC_RETRIEVE_CNF, leg->callref); + /* In case of call waiting/swap, At this point we need to tell the MSC to send + * audio to the port of the original call + */ + leg->state = MNCC_CC_CONNECTED; + send_rtp_connect(leg, other_leg); } static void check_stp_cnf(struct mncc_connection *conn, const char *buf, int rc) @@ -763,6 +867,7 @@ leg->base.ring_call = mncc_call_leg_ring; leg->base.release_call = mncc_call_leg_release; leg->base.call = call; + leg->base.update_rtp = update_rtp; leg->callref = call->id; @@ -775,8 +880,14 @@ mncc.fields |= MNCC_F_CALLING; mncc.calling.plan = GSM48_NPI_ISDN_E164; - mncc.calling.type = GSM48_TON_UNKNOWN; - osmo_strlcpy(mncc.calling.number, call->source, sizeof(mncc.calling.number)); + + if (call->source && call->source[0] == '+') { + mncc.calling.type = GSM48_TON_INTERNATIONAL; + OSMO_STRLCPY_ARRAY(mncc.calling.number, call->source + 1); + } else { + mncc.calling.type = GSM48_TON_UNKNOWN; + OSMO_STRLCPY_ARRAY(mncc.calling.number, call->source); + } if (conn->app->use_imsi_as_id) { snprintf(mncc.imsi, 15, "%s", call->dest); @@ -784,7 +895,7 @@ mncc.fields |= MNCC_F_CALLED; mncc.called.plan = GSM48_NPI_ISDN_E164; mncc.called.type = GSM48_TON_UNKNOWN; - osmo_strlcpy(mncc.called.number, call->dest, sizeof(mncc.called.number)); + OSMO_STRLCPY_ARRAY(mncc.called.number, call->dest); } /* @@ -817,6 +928,7 @@ LOGP(DMNCC, LOGL_ERROR, "Failed to connect(%s). Retrying\n", conn->app->mncc.path); conn->state = MNCC_DISCONNECTED; + conn->fd.fd = -1; osmo_timer_schedule(&conn->reconnect, 5, 0); return; } @@ -888,6 +1000,9 @@ case MNCC_HOLD_IND: check_hold_ind(conn, buf, rc); break; + case MNCC_RETRIEVE_IND: + check_retrieve_ind(conn, buf, rc); + break; case MNCC_START_DTMF_IND: check_dtmf_start(conn, buf, rc); break; @@ -912,6 +1027,7 @@ conn->reconnect.data = conn; conn->fd.cb = mncc_data; conn->fd.data = conn; + conn->fd.fd = -1; conn->app = cfg; conn->state = MNCC_DISCONNECTED; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/src/mncc_protocol.h new/osmo-sip-connector-1.4.0/src/mncc_protocol.h --- old/osmo-sip-connector-1.2.0/src/mncc_protocol.h 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/src/mncc_protocol.h 2020-01-02 21:35:01.000000000 +0100 @@ -158,6 +158,9 @@ unsigned char lchan_type; unsigned char lchan_mode; + + /* A buffer to contain SDP ('\0' terminated) */ + char sdp[1024]; }; struct gsm_data_frame { @@ -166,7 +169,7 @@ unsigned char data[0]; }; -#define MNCC_SOCK_VERSION 5 +#define MNCC_SOCK_VERSION 6 struct gsm_mncc_hello { uint32_t msg_type; uint32_t version; @@ -189,6 +192,7 @@ uint16_t port; uint32_t payload_type; uint32_t payload_msg_type; + char sdp[1024]; }; struct gsm_mncc_bridge { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/src/sdp.c new/osmo-sip-connector-1.4.0/src/sdp.c --- old/osmo-sip-connector-1.2.0/src/sdp.c 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/src/sdp.c 2020-01-02 21:35:01.000000000 +0100 @@ -33,6 +33,45 @@ #include <string.h> /* + * Check if the media mode attribute exists in SDP, in this + * case update the passed pointer with the media mode + */ +bool sdp_get_sdp_mode(const sip_t *sip, sdp_mode_t *mode) { + + const char *sdp_data; + sdp_parser_t *parser; + sdp_session_t *sdp; + + if (!sip->sip_payload || !sip->sip_payload->pl_data) { + LOGP(DSIP, LOGL_ERROR, "No SDP file\n"); + return false; + } + + sdp_data = sip->sip_payload->pl_data; + parser = sdp_parse(NULL, sdp_data, strlen(sdp_data), sdp_f_mode_0000); + if (!parser) { + LOGP(DSIP, LOGL_ERROR, "Failed to parse SDP\n"); + return false; + } + + sdp = sdp_session(parser); + if (!sdp) { + LOGP(DSIP, LOGL_ERROR, "No sdp session\n"); + sdp_parser_free(parser); + return false; + } + + if (!sdp->sdp_media || !sdp->sdp_media->m_mode) { + sdp_parser_free(parser); + return sdp_sendrecv; + } + + sdp_parser_free(parser); + *mode = sdp->sdp_media->m_mode; + return true; +} + +/* * We want to decide on the audio codec later but we need to see * if it is even including some of the supported ones. */ @@ -163,16 +202,37 @@ return true; } -char *sdp_create_file(struct sip_call_leg *leg, struct call_leg *other) +char *sdp_create_file(struct sip_call_leg *leg, struct call_leg *other, sdp_mode_t mode) { - struct in_addr net = { .s_addr = htonl(other->ip) }; + struct in_addr net = { .s_addr = other->ip }; char *fmtp_str = NULL, *sdp; + char *mode_attribute; + char ip_addr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &net, ip_addr, sizeof(ip_addr)); leg->wanted_codec = app_media_name(other->payload_msg_type); if (strcmp(leg->wanted_codec, "AMR") == 0) fmtp_str = talloc_asprintf(leg, "a=fmtp:%d octet-align=1\r\n", other->payload_type); + switch (mode) { + case sdp_inactive: + mode_attribute = "a=inactive\r\n"; + break; + case sdp_sendrecv: + mode_attribute = "a=sendrecv\r\n"; + break; + case sdp_sendonly: + mode_attribute = "a=sendonly\r\n"; + break; + case sdp_recvonly: + mode_attribute = "a=recvonly\r\n"; + break; + default: + OSMO_ASSERT(false); + break; + } + sdp = talloc_asprintf(leg, "v=0\r\n" "o=Osmocom 0 0 IN IP4 %s\r\n" @@ -181,12 +241,14 @@ "t=0 0\r\n" "m=audio %d RTP/AVP %d\r\n" "%s" - "a=rtpmap:%d %s/8000\r\n", - inet_ntoa(net), inet_ntoa(net), /* never use diff. addr! */ + "a=rtpmap:%d %s/8000\r\n" + "%s", + ip_addr, ip_addr, other->port, other->payload_type, fmtp_str ? fmtp_str : "", other->payload_type, - leg->wanted_codec); + leg->wanted_codec, + mode_attribute); talloc_free(fmtp_str); return sdp; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/src/sdp.h new/osmo-sip-connector-1.4.0/src/sdp.h --- old/osmo-sip-connector-1.2.0/src/sdp.h 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/src/sdp.h 2020-01-02 21:35:01.000000000 +0100 @@ -1,13 +1,15 @@ #pragma once #include <sofia-sip/sip.h> +#include <sofia-sip/sdp.h> #include <stdbool.h> struct sip_call_leg; struct call_leg; +bool sdp_get_sdp_mode(const sip_t *sip, sdp_mode_t *mode); bool sdp_screen_sdp(const sip_t *sip); bool sdp_extract_sdp(struct sip_call_leg *leg, const sip_t *sip, bool any_codec); -char *sdp_create_file(struct sip_call_leg *, struct call_leg *); +char *sdp_create_file(struct sip_call_leg *, struct call_leg *, sdp_mode_t mode); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/src/sip.c new/osmo-sip-connector-1.4.0/src/sip.c --- old/osmo-sip-connector-1.2.0/src/sip.c 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/src/sip.c 2020-01-02 21:35:01.000000000 +0100 @@ -28,6 +28,7 @@ #include <sofia-sip/sip_status.h> #include <sofia-sip/su_log.h> +#include <sofia-sip/sdp.h> #include <talloc.h> @@ -39,6 +40,30 @@ static void sip_ring_call(struct call_leg *_leg); static void sip_connect_call(struct call_leg *_leg); static void sip_dtmf_call(struct call_leg *_leg, int keypad); +static void sip_hold_call(struct call_leg *_leg); +static void sip_retrieve_call(struct call_leg *_leg); + + +/* Find a SIP Call leg by given nua_handle */ +static struct sip_call_leg *sip_find_leg(nua_handle_t *nh) +{ + struct call *call; + + llist_for_each_entry(call, &g_call_list, entry) { + if (call->initial && call->initial->type == CALL_TYPE_SIP) { + struct sip_call_leg *leg = (struct sip_call_leg *) call->initial; + if (leg->nua_handle == nh) + return leg; + } + if (call->remote && call->remote->type == CALL_TYPE_SIP) { + struct sip_call_leg *leg = (struct sip_call_leg *) call->remote; + if (leg->nua_handle == nh) + return leg; + } + } + + return NULL; +} static void call_progress(struct sip_call_leg *leg, const sip_t *sip, int status) { @@ -51,7 +76,7 @@ if (status == 183) sdp_extract_sdp(leg, sip, false); - LOGP(DSIP, LOGL_NOTICE, "leg(%p) is now ringing.\n", leg); + LOGP(DSIP, LOGL_INFO, "leg(%p) is now progressing.\n", leg); other->ring_call(other); } @@ -73,7 +98,7 @@ return; } - LOGP(DSIP, LOGL_NOTICE, "leg(%p) is now connected.\n", leg); + LOGP(DSIP, LOGL_INFO, "leg(%p) is now connected(%s).\n", leg, sip->sip_call_id->i_id); leg->state = SIP_CC_CONNECTED; other->connect_call(other); nua_ack(leg->nua_handle, TAG_END()); @@ -85,8 +110,9 @@ struct call *call; struct sip_call_leg *leg; const char *from = NULL, *to = NULL; + char ip_addr[INET_ADDRSTRLEN]; - LOGP(DSIP, LOGL_DEBUG, "Incoming call handle(%p)\n", nh); + LOGP(DSIP, LOGL_INFO, "Incoming call(%s) handle(%p)\n", sip->sip_call_id->i_id, nh); if (!sdp_screen_sdp(sip)) { LOGP(DSIP, LOGL_ERROR, "No supported codec.\n"); @@ -124,7 +150,7 @@ * not this place. It starts with the TCH/F vs. TCH/H selection based * on the offered codecs, and then RTP_CREATE should have it. So both * are GSM related... and do not belong here. Just pick the first codec - * so the IP addresss port and payload type is set. + * so the IP address, port and payload type is set. */ if (!sdp_extract_sdp(leg, sip, true)) { LOGP(DSIP, LOGL_ERROR, "leg(%p) no audio, releasing\n", leg); @@ -133,11 +159,19 @@ call_leg_release(&leg->base); return; } + struct in_addr net = { .s_addr = leg->base.ip }; + inet_ntop(AF_INET, &net, ip_addr, sizeof(ip_addr)); + LOGP(DSIP, LOGL_INFO, "SDP Extracted: IP=(%s) PORT=(%u) PAYLOAD=(%u).\n", + ip_addr, + leg->base.port, + leg->base.payload_type); leg->base.release_call = sip_release_call; leg->base.ring_call = sip_ring_call; leg->base.connect_call = sip_connect_call; leg->base.dtmf = sip_dtmf_call; + leg->base.hold_call = sip_hold_call; + leg->base.retrieve_call = sip_retrieve_call; leg->agent = agent; leg->nua_handle = nh; nua_handle_bind(nh, leg); @@ -148,6 +182,76 @@ talloc_strdup(leg, to)); } +static void sip_handle_reinvite(struct sip_call_leg *leg, nua_handle_t *nh, const sip_t *sip) { + + char *sdp; + sdp_mode_t mode = sdp_sendrecv; + uint32_t ip = leg->base.ip; + uint16_t port = leg->base.port; + char ip_addr[INET_ADDRSTRLEN]; + + LOGP(DSIP, LOGL_INFO, "re-INVITE for call %s\n", sip->sip_call_id->i_id); + + struct call_leg *other = call_leg_other(&leg->base); + + if (!other) { + LOGP(DMNCC, LOGL_ERROR, "leg(%p) other leg gone!\n", leg); + sip_release_call(&leg->base); + return; + } + + if (!sdp_get_sdp_mode(sip, &mode)) { + /* re-INVITE with no SDP. + * We should respond with SDP reflecting current session + */ + sdp = sdp_create_file(leg, other, sdp_sendrecv); + nua_respond(nh, SIP_200_OK, + NUTAG_MEDIA_ENABLE(0), + SIPTAG_CONTENT_TYPE_STR("application/sdp"), + SIPTAG_PAYLOAD_STR(sdp), + TAG_END()); + talloc_free(sdp); + return; + } + + struct in_addr net = { .s_addr = leg->base.ip }; + inet_ntop(AF_INET, &net, ip_addr, sizeof(ip_addr)); + LOGP(DSIP, LOGL_DEBUG, "pre re-INVITE have IP:port (%s:%u)\n", ip_addr, leg->base.port); + + if (mode == sdp_sendonly) { + /* SIP side places call on HOLD */ + sdp = sdp_create_file(leg, other, sdp_recvonly); + /* TODO: Tell core network to stop sending RTP ? */ + } else { + /* SIP re-INVITE may want to change media, IP, port */ + if (!sdp_extract_sdp(leg, sip, true)) { + LOGP(DSIP, LOGL_ERROR, "leg(%p) no audio, releasing\n", leg); + nua_respond(nh, SIP_406_NOT_ACCEPTABLE, TAG_END()); + nua_handle_destroy(nh); + call_leg_release(&leg->base); + return; + } + struct in_addr net = { .s_addr = leg->base.ip }; + inet_ntop(AF_INET, &net, ip_addr, sizeof(ip_addr)); + LOGP(DSIP, LOGL_DEBUG, "Media IP:port in re-INVITE: (%s:%u)\n", ip_addr, leg->base.port); + if (ip != leg->base.ip || port != leg->base.port) { + LOGP(DSIP, LOGL_INFO, "re-INVITE changes media connection.\n"); + if (other->update_rtp) + other->update_rtp(leg->base.call->remote); + } + sdp = sdp_create_file(leg, other, sdp_sendrecv); + } + + LOGP(DSIP, LOGL_DEBUG, "Sending 200 response to re-INVITE for mode(%u)\n", mode); + nua_respond(nh, SIP_200_OK, + NUTAG_MEDIA_ENABLE(0), + SIPTAG_CONTENT_TYPE_STR("application/sdp"), + SIPTAG_PAYLOAD_STR(sdp), + TAG_END()); + talloc_free(sdp); + return; +} + /* Sofia SIP definitions come with error code numbers and strings, this * map allows us to reuse the existing definitions. * The map is in priority order. The first matching entry found @@ -219,8 +323,8 @@ void nua_callback(nua_event_t event, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[]) { - LOGP(DSIP, LOGL_DEBUG, "SIP event(%u) status(%d) phrase(%s) %p\n", - event, status, phrase, hmagic); + LOGP(DSIP, LOGL_DEBUG, "SIP event[%s] status(%d) phrase(%s) %p\n", + nua_event_name(event), status, phrase, hmagic); if (event == nua_r_invite) { struct sip_call_leg *leg; @@ -234,26 +338,41 @@ if (status == 180 || status == 183) call_progress(leg, sip, status); - else if (status == 200) - call_connect(leg, sip); + else if (status == 200) { + if (leg->state == SIP_CC_CONNECTED || leg->state == SIP_CC_HOLD) { + /* This 200 is a response to our re-INVITE on + * a connected call. We just need to ACK it. */ + nua_ack(leg->nua_handle, TAG_END()); + } else { + call_connect(leg, sip); + } + } else if (status >= 300) { struct call_leg *other = call_leg_other(&leg->base); - LOGP(DSIP, LOGL_ERROR, "leg(%p) unknown SIP status(%d), releasing.\n", leg, status); + LOGP(DSIP, LOGL_INFO, "INVITE got status(%d), releasing leg(%p).\n", status, leg); nua_cancel(leg->nua_handle, TAG_END()); nua_handle_destroy(leg->nua_handle); call_leg_release(&leg->base); if (other) { - LOGP(DSIP, LOGL_DEBUG, "Releasing other leg (%p) with status(%d)\n", other, status); + LOGP(DSIP, LOGL_INFO, "Releasing MNCC leg (%p) with status(%d)\n", other, status); other->cause = status2cause(status); other->release_call(other); } } + } else if (event == nua_i_ack) { + /* SDP comes back to us in 200 ACK after we + * respond to the re-INVITE query. */ + if (sip->sip_payload && sip->sip_payload->pl_data) { + struct sip_call_leg *leg = sip_find_leg(nh); + if (leg) + sip_handle_reinvite(leg, nh, sip); + } } else if (event == nua_r_bye || event == nua_r_cancel) { /* our bye or hang up is answered */ struct sip_call_leg *leg = (struct sip_call_leg *) hmagic; - LOGP(DSIP, LOGL_NOTICE, "leg(%p) got resp to %s\n", + LOGP(DSIP, LOGL_INFO, "leg(%p) got resp to %s\n", leg, event == nua_r_bye ? "bye" : "cancel"); nua_handle_destroy(leg->nua_handle); call_leg_release(&leg->base); @@ -262,22 +381,28 @@ struct sip_call_leg *leg = (struct sip_call_leg *) hmagic; struct call_leg *other = call_leg_other(&leg->base); - LOGP(DSIP, LOGL_ERROR, "leg(%p) got bye, releasing.\n", leg); + LOGP(DSIP, LOGL_INFO, "leg(%p) got bye, releasing.\n", leg); nua_handle_destroy(leg->nua_handle); call_leg_release(&leg->base); if (other) other->release_call(other); } else if (event == nua_i_invite) { - /* new incoming leg */ + /* new incoming leg or re-INVITE */ + LOGP(DSIP, LOGL_INFO, "Processing INVITE Call-ID: %s\n", sip->sip_call_id->i_id); - if (status == 100) - new_call((struct sip_agent *) magic, nh, sip); + if (status == 100) { + struct sip_call_leg *leg = sip_find_leg(nh); + if (leg) + sip_handle_reinvite(leg, nh, sip); + else + new_call((struct sip_agent *) magic, nh, sip); + } } else if (event == nua_i_cancel) { struct sip_call_leg *leg; struct call_leg *other; - LOGP(DSIP, LOGL_ERROR, "Canceled on leg(%p)\n", hmagic); + LOGP(DSIP, LOGL_INFO, "Cancelled on leg(%p)\n", hmagic); leg = (struct sip_call_leg *) hmagic; other = call_leg_other(&leg->base); @@ -286,6 +411,8 @@ call_leg_release(&leg->base); if (other) other->release_call(other); + } else { + LOGP(DSIP, LOGL_DEBUG, "Did not handle event[%s] status(%d)\n", nua_event_name(event), status); } } @@ -327,18 +454,18 @@ * to help us here. */ - LOGP(DSIP, LOGL_DEBUG, "%s(): Release with MNCC cause(%s)\n", __func__, gsm48_cc_cause_name(_leg->cause)); + LOGP(DSIP, LOGL_INFO, "%s(): Release with MNCC cause(%s)\n", __func__, gsm48_cc_cause_name(_leg->cause)); cause2status(_leg->cause, &sip_cause, &sip_phrase, &reason_text); snprintf(reason, sizeof reason, "Q.850;cause=%u;text=\"%s\"", _leg->cause, reason_text); switch (leg->state) { case SIP_CC_INITIAL: - LOGP(DSIP, LOGL_NOTICE, "Canceling leg(%p) in int state\n", leg); + LOGP(DSIP, LOGL_INFO, "Cancelling leg(%p) in initial state\n", leg); nua_handle_destroy(leg->nua_handle); call_leg_release(&leg->base); break; case SIP_CC_DLG_CNFD: - LOGP(DSIP, LOGL_NOTICE, "Canceling leg(%p) in cnfd state\n", leg); + LOGP(DSIP, LOGL_INFO, "Cancelling leg(%p) in confirmed state\n", leg); if (leg->dir == SIP_DIR_MT) nua_cancel(leg->nua_handle, TAG_END()); else { @@ -350,7 +477,8 @@ } break; case SIP_CC_CONNECTED: - LOGP(DSIP, LOGL_NOTICE, "Ending leg(%p) in con\n", leg); + case SIP_CC_HOLD: + LOGP(DSIP, LOGL_NOTICE, "Ending leg(%p) in connected state.\n", leg); nua_bye(leg->nua_handle, TAG_END()); break; } @@ -385,7 +513,7 @@ return; } - sdp = sdp_create_file(leg, other); + sdp = sdp_create_file(leg, other, sdp_sendrecv); leg->state = SIP_CC_CONNECTED; nua_respond(leg->nua_handle, SIP_200_OK, @@ -412,6 +540,50 @@ talloc_free(buf); } +static void sip_hold_call(struct call_leg *_leg) +{ + struct sip_call_leg *leg; + struct call_leg *other_leg; + OSMO_ASSERT(_leg->type == CALL_TYPE_SIP); + leg = (struct sip_call_leg *) _leg; + other_leg = call_leg_other(&leg->base); + if (!other_leg) { + LOGP(DMNCC, LOGL_ERROR, "leg(%p) other leg gone!\n", leg); + sip_release_call(&leg->base); + return; + } + char *sdp = sdp_create_file(leg, other_leg, sdp_sendonly); + nua_invite(leg->nua_handle, + NUTAG_MEDIA_ENABLE(0), + SIPTAG_CONTENT_TYPE_STR("application/sdp"), + SIPTAG_PAYLOAD_STR(sdp), + TAG_END()); + talloc_free(sdp); + leg->state = SIP_CC_HOLD; +} + +static void sip_retrieve_call(struct call_leg *_leg) +{ + struct sip_call_leg *leg; + struct call_leg *other_leg; + OSMO_ASSERT(_leg->type == CALL_TYPE_SIP); + leg = (struct sip_call_leg *) _leg; + other_leg = call_leg_other(&leg->base); + if (!other_leg) { + LOGP(DMNCC, LOGL_ERROR, "leg(%p) other leg gone!\n", leg); + sip_release_call(&leg->base); + return; + } + char *sdp = sdp_create_file(leg, other_leg, sdp_sendrecv); + nua_invite(leg->nua_handle, + NUTAG_MEDIA_ENABLE(0), + SIPTAG_CONTENT_TYPE_STR("application/sdp"), + SIPTAG_PAYLOAD_STR(sdp), + TAG_END()); + talloc_free(sdp); + leg->state = SIP_CC_CONNECTED; +} + static int send_invite(struct sip_agent *agent, struct sip_call_leg *leg, const char *calling_num, const char *called_num) { @@ -425,7 +597,7 @@ called_num, agent->app->sip.remote_addr, agent->app->sip.remote_port); - char *sdp = sdp_create_file(leg, other); + char *sdp = sdp_create_file(leg, other, sdp_sendrecv); leg->state = SIP_CC_INITIAL; leg->dir = SIP_DIR_MT; @@ -459,6 +631,8 @@ leg->base.call = call; leg->base.release_call = sip_release_call; leg->base.dtmf = sip_dtmf_call; + leg->base.hold_call = sip_hold_call; + leg->base.retrieve_call = sip_retrieve_call; leg->agent = agent; leg->nua_handle = nua_handle(agent->nua, leg, TAG_END()); @@ -492,7 +666,19 @@ * the log handler call-back function, so we have no clue what log level the * currently logged message was sent for :( As a result, we can only use one * hard-coded LOGL_NOTICE here */ - osmo_vlogp(DSIP, LOGL_NOTICE, "", 0, 0, fmt, ap); + if (!log_check_level(DSIP, LOGL_NOTICE)) + return; + /* The sofia-sip log line *sometimes* lacks a terminating '\n'. Add it. */ + char log_line[256]; + int rc = vsnprintf(log_line, sizeof(log_line), fmt, ap); + if (rc > 0) { + /* since we're explicitly checking for sizeof(log_line), we can use vsnprintf()'s return value (which, + * alone, would possibly cause writing past the buffer's end). */ + char *end = log_line + OSMO_MIN(rc, sizeof(log_line) - 2); + osmo_strlcpy(end, "\n", 2); + LOGP(DSIP, LOGL_NOTICE, "%s", log_line); + } else + LOGP(DSIP, LOGL_NOTICE, "unknown logging from sip\n"); } void sip_agent_init(struct sip_agent *agent, struct app_config *app) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osmo-sip-connector-1.2.0/src/vty.c new/osmo-sip-connector-1.4.0/src/vty.c --- old/osmo-sip-connector-1.2.0/src/vty.c 2019-01-21 18:54:09.000000000 +0100 +++ new/osmo-sip-connector-1.4.0/src/vty.c 2020-01-02 21:35:01.000000000 +0100 @@ -129,7 +129,7 @@ DEFUN(cfg_sip_remote_addr, cfg_sip_remote_addr_cmd, "remote ADDR <1-65534>", - "Remore information\nSIP hostname\nport\n") + "Remote information\nSIP hostname\nport\n") { talloc_free((char *) g_app.sip.remote_addr); g_app.sip.remote_addr = talloc_strdup(tall_mncc_ctx, argv[0]);