Fixed #1537: Via rewrite: putting the right IP address in Via sent-by for outgoing requests



git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@4173 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjsip/include/pjsip-simple/publish.h b/pjsip/include/pjsip-simple/publish.h
index c1a97fb..59a5870 100644
--- a/pjsip/include/pjsip-simple/publish.h
+++ b/pjsip/include/pjsip-simple/publish.h
@@ -237,6 +237,22 @@
 						const pjsip_hdr *hdr_list);
 
 /**
+ * Set the "sent-by" field of the Via header for outgoing requests.
+ *
+ * @param pubc	    The client publication structure.
+ * @param via_addr  Set via_addr to use for the Via header or NULL to use
+ *                  the transport's published name.
+ * @param via_tp    via_addr will only be used if we are using via_tp
+ *                  transport.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_publishc_set_via_sent_by(pjsip_publishc *pubc,
+				                    pjsip_host_port *via_addr,
+                                                    pjsip_transport *via_tp);
+
+
+/**
  * Create PUBLISH request for the specified client publication structure.
  * Application can use this function to both create initial publication
  * or to modify existing publication. 
diff --git a/pjsip/include/pjsip-ua/sip_regc.h b/pjsip/include/pjsip-ua/sip_regc.h
index c2aec0c..9e39381 100644
--- a/pjsip/include/pjsip-ua/sip_regc.h
+++ b/pjsip/include/pjsip-ua/sip_regc.h
@@ -191,6 +191,21 @@
 				     pj_uint32_t expires);
 
 /**
+ * Set the "sent-by" field of the Via header for outgoing requests.
+ *
+ * @param regc	    The client registration structure.
+ * @param via_addr  Set via_addr to use for the Via header or NULL to use
+ *                  the transport's published name.
+ * @param via_tp    via_addr will only be used if we are using via_tp
+ *                  transport.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_set_via_sent_by(pjsip_regc *regc,
+				                pjsip_host_port *via_addr,
+                                                pjsip_transport *via_tp);
+
+/**
  * Set the number of seconds to refresh the client registration before
  * the registration expires.
  *
diff --git a/pjsip/include/pjsip/sip_dialog.h b/pjsip/include/pjsip/sip_dialog.h
index a9922d5..9f37c32 100644
--- a/pjsip/include/pjsip/sip_dialog.h
+++ b/pjsip/include/pjsip/sip_dialog.h
@@ -173,6 +173,14 @@
 
     /** Module specific data. */
     void	       *mod_data[PJSIP_MAX_MODULE]; /**< Module data.	    */
+
+    /**
+     * If via_addr is set, it will be used as the "sent-by" field of the
+     * Via header for outgoing requests as long as the request uses via_tp
+     * transport. Normally application should not use or access these fields.
+     */
+    pjsip_host_port     via_addr;   /**< Via address.	                    */
+    const void         *via_tp;     /**< Via transport.	                    */
 };
 
 
@@ -298,6 +306,22 @@
 
 
 /**
+ * Set the "sent-by" field of the Via header for outgoing requests.
+ *
+ * @param dlg	    The dialog instance.
+ * @param via_addr  Set via_addr to use for the Via header or NULL to use
+ *                  the transport's published name.
+ * @param via_tp    via_addr will only be used if we are using via_tp
+ *                  transport.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_set_via_sent_by(pjsip_dialog *dlg,
+				               pjsip_host_port *via_addr,
+                                               pjsip_transport *via_tp);
+
+
+/**
  * Create a new (forked) dialog on receipt on forked response in rdata. 
  * The new dialog will be created from original_dlg, except that it will have
  * new remote tag as copied from the To header in the response. Upon return, 
@@ -402,7 +426,7 @@
  * 			    registered as a usage to the dialog.
  */
 PJ_DECL(pj_bool_t) pjsip_dlg_has_usage(pjsip_dialog *dlg,
-					  pjsip_module *module);
+				       pjsip_module *module);
 
 /**
  * Attach module specific data to the dialog. Application can also set 
diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h
index 99f006a..20ddcc5 100644
--- a/pjsip/include/pjsip/sip_transport.h
+++ b/pjsip/include/pjsip/sip_transport.h
@@ -584,6 +584,14 @@
      * Arbitrary data attached by PJSIP modules.
      */
     void		    *mod_data[PJSIP_MAX_MODULE];
+
+    /**
+     * If via_addr is set, it will be used as the "sent-by" field of the
+     * Via header for outgoing requests as long as the request uses via_tp
+     * transport. Normally application should not use or access these fields.
+     */
+    pjsip_host_port          via_addr;      /**< Via address.	        */
+    const void              *via_tp;        /**< Via transport.	        */
 };
 
 
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index 8bf935a..a964815 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -2818,6 +2818,16 @@
     int		     contact_rewrite_method;
 
     /**
+     * This option is used to overwrite the "sent-by" field of the Via header
+     * for outgoing messages with the same interface address as the one in
+     * the REGISTER request, as long as the request uses the same transport
+     * instance as the previous REGISTER request.
+     *
+     * Default: 1 (yes)
+     */
+    pj_bool_t        allow_via_rewrite;
+
+    /**
      * Control the use of SIP outbound feature. SIP outbound is described in
      * RFC 5626 to enable proxies or registrar to send inbound requests back
      * to UA using the same connection initiated by the UA for its
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index 911cad1..326f916 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -212,6 +212,9 @@
     pj_str_t         reg_contact;   /**< Contact header for REGISTER.
 				         It may be different than acc
 				         contact if outbound is used    */
+    pjsip_host_port  via_addr;      /**< Address for Via header         */
+    pjsip_transport *via_tp;        /**< Transport associated with
+                                         the Via address                */
 
     pj_str_t	     srv_domain;    /**< Host part of reg server.	*/
     int		     srv_port;	    /**< Port number of reg server.	*/
diff --git a/pjsip/src/pjsip-simple/publishc.c b/pjsip/src/pjsip-simple/publishc.c
index d70442b..6282923 100644
--- a/pjsip/src/pjsip-simple/publishc.c
+++ b/pjsip/src/pjsip-simple/publishc.c
@@ -94,6 +94,8 @@
     pj_uint32_t			 expires;
     pjsip_route_hdr		 route_set;
     pjsip_hdr			 usr_hdr;
+    pjsip_host_port              via_addr;
+    const void                  *via_tp;
 
     /* Authorization sessions. */
     pjsip_auth_clt_sess		 auth_sess;
@@ -345,6 +347,21 @@
     return PJ_SUCCESS;
 }
 
+PJ_DEF(pj_status_t) pjsip_publishc_set_via_sent_by(pjsip_publishc *pubc,
+				                   pjsip_host_port *via_addr,
+                                                   pjsip_transport *via_tp)
+{
+    PJ_ASSERT_RETURN(pubc, PJ_EINVAL);
+
+    if (!via_addr)
+        pj_bzero(&pubc->via_addr, sizeof(pubc->via_addr));
+    else
+        pubc->via_addr = *via_addr;
+    pubc->via_tp = via_tp;
+
+    return PJ_SUCCESS;
+}
+
 static pj_status_t create_request(pjsip_publishc *pubc, 
 				  pjsip_tx_data **p_tdata)
 {
@@ -739,6 +756,12 @@
     }
     pj_mutex_unlock(pubc->mutex);
 
+    /* If via_addr is set, use this address for the Via header. */
+    if (pubc->via_addr.host.slen > 0) {
+        tdata->via_addr = pubc->via_addr;
+        tdata->via_tp = pubc->via_tp;
+    }
+
     /* Invalidate message buffer. */
     pjsip_tx_data_invalidate_msg(tdata);
 
diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c
index c02c17b..2bbc81b 100644
--- a/pjsip/src/pjsip-ua/sip_reg.c
+++ b/pjsip/src/pjsip-ua/sip_reg.c
@@ -90,6 +90,8 @@
     pj_uint32_t			 delay_before_refresh;
     pjsip_route_hdr		 route_set;
     pjsip_hdr			 hdr_list;
+    pjsip_host_port              via_addr;
+    const void                  *via_tp;
 
     /* Authorization sessions. */
     pjsip_auth_clt_sess		 auth_sess;
@@ -808,6 +810,21 @@
     }
 }
 
+PJ_DEF(pj_status_t) pjsip_regc_set_via_sent_by( pjsip_regc *regc,
+				                pjsip_host_port *via_addr,
+                                                pjsip_transport *via_tp)
+{
+    PJ_ASSERT_RETURN(regc, PJ_EINVAL);
+
+    if (!via_addr)
+        pj_bzero(&regc->via_addr, sizeof(regc->via_addr));
+    else
+        regc->via_addr = *via_addr;
+    regc->via_tp = via_tp;
+
+    return PJ_SUCCESS;
+}
+
 PJ_DEF(pj_status_t)
 pjsip_regc_set_delay_before_refresh( pjsip_regc *regc,
 				     pj_uint32_t delay )
@@ -1262,6 +1279,12 @@
      */
     pjsip_tx_data_add_ref(tdata);
 
+    /* If via_addr is set, use this address for the Via header. */
+    if (regc->via_addr.host.slen > 0) {
+        tdata->via_addr = regc->via_addr;
+        tdata->via_tp = regc->via_tp;
+    }
+
     /* Need to unlock the regc temporarily while sending the message to
      * prevent deadlock (https://trac.pjsip.org/repos/ticket/1247).
      * It should be safe to do this since the regc's refcount has been
diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c
index 57f0c14..2e19bfc 100644
--- a/pjsip/src/pjsip/sip_dialog.c
+++ b/pjsip/src/pjsip/sip_dialog.c
@@ -585,6 +585,24 @@
     return PJ_SUCCESS;
 }
 
+/*
+ * Set "sent-by" field of Via header.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_via_sent_by( pjsip_dialog *dlg,
+				               pjsip_host_port *via_addr,
+                                               pjsip_transport *via_tp)
+{
+    PJ_ASSERT_RETURN(dlg, PJ_EINVAL);
+
+    if (!via_addr)
+        pj_bzero(&dlg->via_addr, sizeof(dlg->via_addr));
+    else
+        dlg->via_addr = *via_addr;
+    dlg->via_tp = via_tp;
+
+    return PJ_SUCCESS;
+}
+
 
 /*
  * Create forked dialog from a response.
@@ -1163,6 +1181,12 @@
     /* Lock and increment session */
     pjsip_dlg_inc_lock(dlg);
 
+    /* If via_addr is set, use this address for the Via header. */
+    if (dlg->via_addr.host.slen > 0) {
+        tdata->via_addr = dlg->via_addr;
+        tdata->via_tp = dlg->via_tp;
+    }
+
     /* Update dialog's CSeq and message's CSeq if request is not
      * ACK nor CANCEL.
      */
diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c
index f238f52..ed4fd9c 100644
--- a/pjsip/src/pjsip/sip_util.c
+++ b/pjsip/src/pjsip/sip_util.c
@@ -1192,7 +1192,13 @@
 	}
 
 	via->transport = pj_str(stateless_data->cur_transport->type_name);
-	via->sent_by = stateless_data->cur_transport->local_name;
+        if (tdata->via_addr.host.slen > 0 &&
+            tdata->via_tp == (void *)stateless_data->cur_transport)
+        {
+            via->sent_by = tdata->via_addr;
+        } else {
+	    via->sent_by = stateless_data->cur_transport->local_name;
+        }
 	via->rport_param = pjsip_cfg()->endpt.disable_rport ? -1 : 0;
 
 	pjsip_tx_data_invalidate_msg(tdata);
diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c
index 65e5264..f6c97df 100644
--- a/pjsip/src/pjsua-lib/pjsua_acc.c
+++ b/pjsip/src/pjsua-lib/pjsua_acc.c
@@ -624,6 +624,8 @@
     /* Invalidate */
     acc->valid = PJ_FALSE;
     acc->contact.slen = 0;
+    pj_bzero(&acc->via_addr, sizeof(acc->via_addr));
+    acc->via_tp = NULL;
 
     /* Remove from array */
     for (i=0; i<pjsua_var.acc_cnt; ++i) {
@@ -1090,6 +1092,25 @@
 						cfg->reg_delay_before_refresh);
     }
 
+    /* Allow via rewrite */
+    if (acc->cfg.allow_via_rewrite != cfg->allow_via_rewrite) {
+        if (acc->regc != NULL) {
+            if (cfg->allow_via_rewrite) {
+                pjsip_regc_set_via_sent_by(acc->regc, &acc->via_addr,
+                                           acc->via_tp);
+            } else
+                pjsip_regc_set_via_sent_by(acc->regc, NULL, NULL);
+        }
+        if (acc->publish_sess != NULL) {
+            if (cfg->allow_via_rewrite) {
+                pjsip_publishc_set_via_sent_by(acc->publish_sess,
+                                               &acc->via_addr, acc->via_tp);
+            } else
+                pjsip_publishc_set_via_sent_by(acc->publish_sess, NULL, NULL);
+        }
+        acc->cfg.allow_via_rewrite = cfg->allow_via_rewrite;
+    }
+
     /* Normalize registration timeout and refresh delay */
     if (acc->cfg.reg_uri.slen ) {
         if (acc->cfg.reg_timeout == 0) {
@@ -1314,6 +1335,19 @@
 
     tp = param->rdata->tp_info.transport;
 
+    /* If allow_via_rewrite is enabled, we save the Via "sent-by" address
+     * from the response.
+     */
+    if (acc->cfg.allow_via_rewrite &&
+        (acc->via_addr.host.slen == 0 || acc->via_tp != tp))
+    {
+        via = param->rdata->msg_info.via;
+        if (pj_strcmp(&acc->via_addr.host, &via->sent_by.host))
+            pj_strdup(acc->pool, &acc->via_addr.host, &via->sent_by.host);
+        acc->via_addr.port = via->sent_by.port;
+        acc->via_tp = tp;
+    }
+
     /* Only update if account is configured to auto-update */
     if (acc->cfg.allow_contact_rewrite == PJ_FALSE)
 	return PJ_FALSE;
@@ -2176,6 +2210,14 @@
     }
 
     if (status == PJ_SUCCESS) {
+        if (pjsua_var.acc[acc_id].cfg.allow_via_rewrite &&
+            pjsua_var.acc[acc_id].via_addr.host.slen > 0)
+        {
+            pjsip_regc_set_via_sent_by(pjsua_var.acc[acc_id].regc,
+                                       &pjsua_var.acc[acc_id].via_addr,
+                                       pjsua_var.acc[acc_id].via_tp);
+        }
+
 	//pjsua_process_msg_data(tdata, NULL);
 	status = pjsip_regc_send( pjsua_var.acc[acc_id].regc, tdata );
     }
@@ -2541,6 +2583,14 @@
 	pjsip_tx_data_set_transport(tdata, &tp_sel);
     }
 
+    /* If via_addr is set, use this address for the Via header. */
+    if (pjsua_var.acc[acc_id].cfg.allow_via_rewrite &&
+        pjsua_var.acc[acc_id].via_addr.host.slen > 0)
+    {
+        tdata->via_addr = pjsua_var.acc[acc_id].via_addr;
+        tdata->via_tp = pjsua_var.acc[acc_id].via_tp;
+    }
+
     /* Done */
     *p_tdata = tdata;
     return PJ_SUCCESS;
diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c
index ccc0653..ab7fd1f 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -715,6 +715,9 @@
      */
     pjsip_dlg_inc_lock(dlg);
 
+    if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0)
+        pjsip_dlg_set_via_sent_by(dlg, &acc->via_addr, acc->via_tp);
+
     /* Calculate call's secure level */
     call->secure_level = get_secure_level(acc_id, dest_uri);
 
@@ -1170,6 +1173,13 @@
 	goto on_return;
     }
 
+    if (pjsua_var.acc[acc_id].cfg.allow_via_rewrite &&
+        pjsua_var.acc[acc_id].via_addr.host.slen > 0)
+    {
+        pjsip_dlg_set_via_sent_by(dlg, &pjsua_var.acc[acc_id].via_addr,
+                                  pjsua_var.acc[acc_id].via_tp);
+    }
+
     /* Set credentials */
     if (pjsua_var.acc[acc_id].cred_cnt) {
 	pjsip_auth_clt_set_credentials(&dlg->auth_sess, 
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 4fbfcb1..08de030 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -211,6 +211,7 @@
     cfg->unpublish_max_wait_time_msec = PJSUA_UNPUBLISH_MAX_WAIT_TIME_MSEC;
     cfg->transport_id = PJSUA_INVALID_ID;
     cfg->allow_contact_rewrite = PJ_TRUE;
+    cfg->allow_via_rewrite = PJ_TRUE;
     cfg->require_100rel = pjsua_var.ua_cfg.require_100rel;
     cfg->use_timer = pjsua_var.ua_cfg.use_timer;
     cfg->timer_setting = pjsua_var.ua_cfg.timer_setting;
diff --git a/pjsip/src/pjsua-lib/pjsua_im.c b/pjsip/src/pjsua-lib/pjsua_im.c
index a1062c6..7c6886a 100644
--- a/pjsip/src/pjsua-lib/pjsua_im.c
+++ b/pjsip/src/pjsua-lib/pjsua_im.c
@@ -601,6 +601,12 @@
     /* Add route set */
     pjsua_set_msg_route_set(tdata, &acc->route_set);
 
+    /* If via_addr is set, use this address for the Via header. */
+    if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) {
+        tdata->via_addr = acc->via_addr;
+        tdata->via_tp = acc->via_tp;
+    }
+
     /* Send request (statefully) */
     status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1, 
 				       im_data, &im_callback);
@@ -684,6 +690,12 @@
     /* Add route set */
     pjsua_set_msg_route_set(tdata, &acc->route_set);
 
+    /* If via_addr is set, use this address for the Via header. */
+    if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) {
+        tdata->via_addr = acc->via_addr;
+        tdata->via_tp = acc->via_tp;
+    }
+
     /* Create data to reauthenticate */
     im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
     im_data->acc_id = acc_id;
diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c
index ff30879..5eac441 100644
--- a/pjsip/src/pjsua-lib/pjsua_pres.c
+++ b/pjsip/src/pjsua-lib/pjsua_pres.c
@@ -864,6 +864,9 @@
 	return PJ_TRUE;
     }
 
+    if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0)
+        pjsip_dlg_set_via_sent_by(dlg, &acc->via_addr, acc->via_tp);
+
     /* Set credentials and preference. */
     pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
     pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
@@ -1229,6 +1232,12 @@
     /* Add headers etc */
     pjsua_process_msg_data(tdata, NULL);
 
+    /* Set Via sent-by */
+    if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) {
+        pjsip_publishc_set_via_sent_by(acc->publish_sess, &acc->via_addr,
+                                       acc->via_tp);
+    }
+
     /* Send the PUBLISH request */
     status = pjsip_publishc_send(acc->publish_sess, tdata);
     if (status == PJ_EPENDING) {
@@ -1780,6 +1789,9 @@
      */
     pjsip_dlg_inc_lock(buddy->dlg);
 
+    if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0)
+        pjsip_dlg_set_via_sent_by(buddy->dlg, &acc->via_addr, acc->via_tp);
+
     status = pjsip_pres_create_uac( buddy->dlg, &pres_callback, 
 				    PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
     if (status != PJ_SUCCESS) {
@@ -2097,6 +2109,9 @@
      */
     pjsip_dlg_inc_lock(acc->mwi_dlg);
 
+    if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0)
+        pjsip_dlg_set_via_sent_by(acc->mwi_dlg, &acc->via_addr, acc->via_tp);
+
     /* Create UAC subscription */
     status = pjsip_mwi_create_uac(acc->mwi_dlg, &mwi_cb, 
 				  PJSIP_EVSUB_NO_EVENT_ID, &acc->mwi_sub);