[calls] sip: Use per origin IP instead of a global IP



commit 8b126484cba5b47871e5fa767376a3f746c2cf76
Author: Evangelos Ribeiro Tzaras <devrtz fortysixandtwo eu>
Date:   Tue Nov 23 00:58:08 2021 +0100

    sip: Use per origin IP instead of a global IP
    
    Sofia detects a NAT by presence of the "received" parameter in the Via header in
    the response to a REGISTER. Sofia will then update the Contact header to use the
    IP as reported by the registrar.
    
    The "received" parameter MUST be included in the response according to
    https://datatracker.ietf.org/doc/html/rfc3261#section-18.2.1
    when the registrar detects a difference between the domain part of the top Via
    header and the packet source address but practice has shown that this will not
    always be the case.
    
    Addditionally this change allows us to have origins bound to different network
    interfaces which would be useful when a registrar can only be accessed through a
    VPN.
    
    This also fixes an issue with SDP introduced in
    36880c3d34f0781811c396748f31aeddd5128031 which was only seen on some SIP
    providers:
    
    The session name ("s=") line is not relevant for establishing a connection,
    the connection data (c=") line is.
    
    See https://datatracker.ietf.org/doc/html/rfc4566 section 5.3 and 5.7

 plugins/sip/calls-sip-call.c          | 23 +++++++++++--
 plugins/sip/calls-sip-call.h          |  1 +
 plugins/sip/calls-sip-media-manager.c | 63 +++++++++++++++++++++++++++++++++--
 plugins/sip/calls-sip-media-manager.h | 14 ++++----
 plugins/sip/calls-sip-origin.c        | 12 +++++--
 tests/test-sip.c                      | 12 +++----
 6 files changed, 105 insertions(+), 20 deletions(-)
---
diff --git a/plugins/sip/calls-sip-call.c b/plugins/sip/calls-sip-call.c
index 8a1981f0..c75e3927 100644
--- a/plugins/sip/calls-sip-call.c
+++ b/plugins/sip/calls-sip-call.c
@@ -50,6 +50,7 @@
 enum {
   PROP_0,
   PROP_CALL_HANDLE,
+  PROP_IP,
   PROP_LAST_PROP
 };
 static GParamSpec *props[PROP_LAST_PROP];
@@ -61,6 +62,8 @@ struct _CallsSipCall
   CallsSipMediaManager *manager;
   CallsSipMediaPipeline *pipeline;
 
+  char *ip;
+
   guint lport_rtp;
   guint lport_rtcp;
   guint rport_rtp;
@@ -138,6 +141,7 @@ calls_sip_call_answer (CallsCall *call)
   calls_sip_call_setup_local_media_connection (self, local_port, local_port + 1);
 
   local_sdp = calls_sip_media_manager_get_capabilities (self->manager,
+                                                        self->ip,
                                                         local_port,
                                                         FALSE,
                                                         self->codecs);
@@ -205,6 +209,11 @@ calls_sip_call_set_property (GObject      *object,
     self->nh = g_value_get_pointer (value);
     break;
 
+  case PROP_IP:
+    g_free (self->ip);
+    self->ip = g_value_dup_string (value);
+    break;
+
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     break;
@@ -243,6 +252,7 @@ calls_sip_call_finalize (GObject *object)
   }
   g_clear_pointer (&self->codecs, g_list_free);
   g_clear_pointer (&self->remote, g_free);
+  g_clear_pointer (&self->ip, g_free);
 
   G_OBJECT_CLASS (calls_sip_call_parent_class)->finalize (object);
 }
@@ -266,7 +276,14 @@ calls_sip_call_class_init (CallsSipCallClass *klass)
                           "NUA handle",
                           "The used NUA handler",
                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-  g_object_class_install_property (object_class, PROP_CALL_HANDLE, props[PROP_CALL_HANDLE]);
+
+  props[PROP_IP] =
+    g_param_spec_string ("own-ip",
+                         "Own IP",
+                         "Own IP for media and SDP",
+                         NULL,
+                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT);
+  g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
 }
 
 
@@ -353,14 +370,16 @@ calls_sip_call_activate_media (CallsSipCall *self,
 CallsSipCall *
 calls_sip_call_new (const gchar  *id,
                     gboolean      inbound,
+                    const char   *own_ip,
                     nua_handle_t *handle)
 {
   g_return_val_if_fail (id, NULL);
 
   return g_object_new (CALLS_TYPE_SIP_CALL,
-                       "nua-handle", handle,
                        "id", id,
                        "inbound", inbound,
+                       "own-ip", own_ip,
+                       "nua-handle", handle,
                        NULL);
 }
 
diff --git a/plugins/sip/calls-sip-call.h b/plugins/sip/calls-sip-call.h
index ea12b046..5b8e5939 100644
--- a/plugins/sip/calls-sip-call.h
+++ b/plugins/sip/calls-sip-call.h
@@ -37,6 +37,7 @@ G_DECLARE_FINAL_TYPE (CallsSipCall, calls_sip_call, CALLS, SIP_CALL, CallsCall)
 
 CallsSipCall          *calls_sip_call_new                               (const gchar  *number,
                                                                          gboolean      inbound,
+                                                                         const char   *own_ip,
                                                                          nua_handle_t *handle);
 void                   calls_sip_call_setup_remote_media_connection     (CallsSipCall *self,
                                                                          const char   *remote,
diff --git a/plugins/sip/calls-sip-media-manager.c b/plugins/sip/calls-sip-media-manager.c
index 27d7b490..759be4ef 100644
--- a/plugins/sip/calls-sip-media-manager.c
+++ b/plugins/sip/calls-sip-media-manager.c
@@ -52,6 +52,9 @@ typedef struct _CallsSipMediaManager
   GObject parent;
 
   char          *session_ip;
+  int            address_family;
+  struct         addrinfo hints;
+
   CallsSettings *settings;
   GList         *preferred_codecs;
 } CallsSipMediaManager;
@@ -59,6 +62,34 @@ typedef struct _CallsSipMediaManager
 G_DEFINE_TYPE (CallsSipMediaManager, calls_sip_media_manager, G_TYPE_OBJECT);
 
 
+static const char *
+get_address_family_string (CallsSipMediaManager *self,
+                           const char           *ip)
+{
+  struct addrinfo *result = NULL;
+  const char *family;
+
+  if (getaddrinfo (ip, NULL, &self->hints, &result) != 0) {
+    g_warning ("Cannot parse session IP %s", ip);
+    return NULL;
+  }
+
+  /* check if IP is IPv4 or IPv6. We need to specify this in the c= line of SDP */
+  self->address_family = result->ai_family;
+
+  if (result->ai_family == AF_INET)
+    family = "IP4";
+  else if (result->ai_family == AF_INET6)
+    family = "IP6";
+  else
+    family = NULL;
+
+  freeaddrinfo (result);
+
+  return family;
+}
+
+
 static void
 on_notify_preferred_audio_codecs (CallsSipMediaManager *self)
 {
@@ -167,6 +198,10 @@ calls_sip_media_manager_init (CallsSipMediaManager *self)
                             G_CALLBACK (on_notify_preferred_audio_codecs),
                             self);
   on_notify_preferred_audio_codecs (self);
+
+  /* Hints are used with getaddrinfo() when setting the session IP */
+  self->hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_NUMERICHOST;
+  self->hints.ai_family = AF_UNSPEC;
 }
 
 
@@ -198,6 +233,7 @@ calls_sip_media_manager_default (void)
  */
 char *
 calls_sip_media_manager_get_capabilities (CallsSipMediaManager *self,
+                                          const char           *own_ip,
                                           guint                 port,
                                           gboolean              use_srtp,
                                           GList                *supported_codecs)
@@ -206,6 +242,7 @@ calls_sip_media_manager_get_capabilities (CallsSipMediaManager *self,
   g_autoptr (GString) media_line = NULL;
   g_autoptr (GString) attribute_lines = NULL;
   GList *node;
+  const char *address_family_string;
 
   g_return_val_if_fail (CALLS_IS_SIP_MEDIA_MANAGER (self), NULL);
 
@@ -237,12 +274,16 @@ calls_sip_media_manager_get_capabilities (CallsSipMediaManager *self,
   g_string_append_printf (attribute_lines, "a=rtcp:%d\r\n", port + 1);
 
  done:
-  if (self->session_ip && *self->session_ip)
+  if (own_ip && *own_ip)
+    address_family_string = get_address_family_string (self, own_ip);
+
+  if (own_ip && *own_ip && address_family_string)
     return g_strdup_printf ("v=0\r\n"
-                            "s=%s\r\n"
+                            "c=IN %s %s\r\n"
                             "%s\r\n"
                             "%s\r\n",
-                            self->session_ip,
+                            address_family_string,
+                            own_ip,
                             media_line->str,
                             attribute_lines->str);
   else
@@ -266,12 +307,14 @@ calls_sip_media_manager_get_capabilities (CallsSipMediaManager *self,
  */
 char *
 calls_sip_media_manager_static_capabilities (CallsSipMediaManager *self,
+                                             const char           *own_ip,
                                              guint                 port,
                                              gboolean              use_srtp)
 {
   g_return_val_if_fail (CALLS_IS_SIP_MEDIA_MANAGER (self), NULL);
 
   return calls_sip_media_manager_get_capabilities (self,
+                                                   own_ip,
                                                    port,
                                                    use_srtp,
                                                    self->preferred_codecs);
@@ -348,7 +391,21 @@ calls_sip_media_manager_set_session_ip (CallsSipMediaManager *self,
 
   g_clear_pointer (&self->session_ip, g_free);
   if (session_ip && *session_ip) {
+    struct addrinfo *result = NULL;
+
+    if (getaddrinfo (session_ip, NULL, &self->hints, &result) != 0) {
+      g_warning ("Cannot parse session IP %s", session_ip);
+      return;
+    }
+
+    /* check if IP is IPv4 or IPv6. We need to specify this in the c= line of SDP */
+    self->address_family = result->ai_family;
+
     g_debug ("Setting session IP to %s", session_ip);
+
+    g_free (self->session_ip);
     self->session_ip = g_strdup (session_ip);
+
+    freeaddrinfo (result);
   }
 }
diff --git a/plugins/sip/calls-sip-media-manager.h b/plugins/sip/calls-sip-media-manager.h
index 428c49d2..279e7275 100644
--- a/plugins/sip/calls-sip-media-manager.h
+++ b/plugins/sip/calls-sip-media-manager.h
@@ -39,14 +39,16 @@ G_DECLARE_FINAL_TYPE (CallsSipMediaManager, calls_sip_media_manager, CALLS, SIP_
 
 CallsSipMediaManager* calls_sip_media_manager_default                       (void);
 gchar*                calls_sip_media_manager_get_capabilities              (CallsSipMediaManager *self,
-                                                                             guint port,
-                                                                             gboolean use_srtp,
-                                                                             GList *supported_codecs);
+                                                                             const char           *own_ip,
+                                                                             guint                 port,
+                                                                             gboolean              use_srtp,
+                                                                             GList                
*supported_codecs);
 gchar*                calls_sip_media_manager_static_capabilities           (CallsSipMediaManager *self,
-                                                                             guint port,
-                                                                             gboolean use_srtp);
+                                                                             const char           *own_ip,
+                                                                             guint                 port,
+                                                                             gboolean              use_srtp);
 gboolean              calls_sip_media_manager_supports_media                (CallsSipMediaManager *self,
-                                                                             const char *media_type);
+                                                                             const char           
*media_type);
 MediaCodecInfo*       get_best_codec                                        (CallsSipMediaManager *self);
 GList *               calls_sip_media_manager_codec_candidates              (CallsSipMediaManager *self);
 GList *               calls_sip_media_manager_get_codecs_from_sdp           (CallsSipMediaManager *self,
diff --git a/plugins/sip/calls-sip-origin.c b/plugins/sip/calls-sip-origin.c
index 5890d94a..fc79a5e5 100644
--- a/plugins/sip/calls-sip-origin.c
+++ b/plugins/sip/calls-sip-origin.c
@@ -111,6 +111,8 @@ struct _CallsSipOrigin
   gboolean auto_connect;
   gboolean direct_mode;
   gboolean can_tel;
+
+  char *own_ip;
   gint local_port;
 
   const char *protocol_prefix;
@@ -229,7 +231,7 @@ add_call (CallsSipOrigin *self,
       call_address = address_split[1];
   }
 
-  sip_call = calls_sip_call_new (call_address, inbound, handle);
+  sip_call = calls_sip_call_new (call_address, inbound, self->own_ip, handle);
   g_assert (sip_call != NULL);
 
   if (self->oper->call_handle)
@@ -251,6 +253,7 @@ add_call (CallsSipOrigin *self,
     calls_sip_call_setup_local_media_connection (sip_call, local_port, local_port + 1);
 
     local_sdp = calls_sip_media_manager_static_capabilities (self->media_manager,
+                                                             self->own_ip,
                                                              local_port,
                                                              FALSE);
 
@@ -424,9 +427,10 @@ sip_r_register (int              status,
     g_debug ("REGISTER successful");
     origin->state = CALLS_ACCOUNT_ONLINE;
     nua_get_params (nua, TAG_ANY (), TAG_END());
+
     if (sip->sip_contact && sip->sip_contact->m_url && sip->sip_contact->m_url->url_host) {
-      calls_sip_media_manager_set_session_ip (origin->media_manager,
-                                              sip->sip_contact->m_url->url_host);
+      g_free (origin->own_ip);
+      origin->own_ip = g_strdup (sip->sip_contact->m_url->url_host);
     }
 
   } else if (status == 401 || status == 407) {
@@ -1317,6 +1321,8 @@ calls_sip_origin_dispose (GObject *object)
 {
   CallsSipOrigin *self = CALLS_SIP_ORIGIN (object);
 
+  g_clear_pointer (&self->own_ip, g_free);
+
   if (!self->use_direct_connection && self->state == CALLS_ACCOUNT_ONLINE)
     go_online (CALLS_ACCOUNT (self), FALSE);
 
diff --git a/tests/test-sip.c b/tests/test-sip.c
index 8a19cff2..d0847b8c 100644
--- a/tests/test-sip.c
+++ b/tests/test-sip.c
@@ -445,7 +445,7 @@ test_sip_media_manager (void)
 
   /* PCMA RTP */
   sdp_message =
-    calls_sip_media_manager_get_capabilities (manager, 40002, FALSE, codecs);
+    calls_sip_media_manager_get_capabilities (manager, NULL, 40002, FALSE, codecs);
 
   g_assert_true (sdp_message);
   g_assert_true (find_string_in_sdp_message (sdp_message,
@@ -461,7 +461,7 @@ test_sip_media_manager (void)
 
   /* PCMA SRTP */
   sdp_message =
-    calls_sip_media_manager_get_capabilities (manager, 42002, TRUE, codecs);
+    calls_sip_media_manager_get_capabilities (manager, NULL, 42002, TRUE, codecs);
   g_assert_true (sdp_message);
   g_assert_true (find_string_in_sdp_message (sdp_message,
                                              "m=audio 42002 RTP/SAVP 8"));
@@ -475,7 +475,7 @@ test_sip_media_manager (void)
   codecs = g_list_append (NULL, media_codec_by_name ("G722"));
 
   sdp_message =
-    calls_sip_media_manager_get_capabilities (manager, 42042, FALSE, codecs);
+    calls_sip_media_manager_get_capabilities (manager, NULL, 42042, FALSE, codecs);
 
   g_assert_true (sdp_message);
   g_assert_true (find_string_in_sdp_message (sdp_message,
@@ -496,7 +496,7 @@ test_sip_media_manager (void)
   codecs = g_list_append (codecs, media_codec_by_name ("PCMA"));
 
   sdp_message =
-    calls_sip_media_manager_get_capabilities (manager, 33340, FALSE, codecs);
+    calls_sip_media_manager_get_capabilities (manager, NULL, 33340, FALSE, codecs);
 
   g_assert_true (sdp_message);
   g_assert_true (find_string_in_sdp_message (sdp_message,
@@ -520,7 +520,7 @@ test_sip_media_manager (void)
   codecs = g_list_append (codecs, media_codec_by_name ("PCMU"));
 
   sdp_message =
-    calls_sip_media_manager_get_capabilities (manager, 18098, TRUE, codecs);
+    calls_sip_media_manager_get_capabilities (manager, NULL, 18098, TRUE, codecs);
 
   g_assert_true (sdp_message);
   g_assert_true (find_string_in_sdp_message (sdp_message,
@@ -535,7 +535,7 @@ test_sip_media_manager (void)
   g_test_expect_message ("CallsSipMediaManager", G_LOG_LEVEL_WARNING,
                          "No supported codecs found. Can't build meaningful SDP message");
   sdp_message =
-    calls_sip_media_manager_get_capabilities (manager, 25048, FALSE, NULL);
+    calls_sip_media_manager_get_capabilities (manager, NULL, 25048, FALSE, NULL);
 
   g_test_assert_expected_messages ();
   g_assert_true (sdp_message);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]