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]);

Reply via email to