Index: /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 741)
+++ /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 742)
@@ -91,5 +91,5 @@
 {
     puts  ("Usage:");
-    puts  ("  pjsua [options]");
+    puts  ("  pjsua [options] [SIP URL to call]");
     puts  ("");
     puts  ("General options:");
@@ -123,4 +123,6 @@
     puts  ("                      TCP and UDP transports on the specified port, unless");
     puts  ("                      if TCP or UDP is disabled.");
+    puts  ("  --ip-addr=IP        Use the specifed address as SIP and RTP addresses.");
+    puts  ("                      (Hint: the IP may be the public IP of the NAT/router)");
     puts  ("  --no-tcp            Disable TCP transport.");
     puts  ("  --no-udp            Disable UDP transport.");
@@ -159,4 +161,7 @@
 
     puts  ("");
+    puts  ("When URL is specified, pjsua will immediately initiate call to that URL");
+    puts  ("");
+
     fflush(stdout);
 }
@@ -269,6 +274,6 @@
     enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL, 
 	   OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, 
-	   OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR,
-	   OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT, 
+	   OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY, 
+	   OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
 	   OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
 	   OPT_USE_STUN1, OPT_USE_STUN2, 
@@ -292,4 +297,5 @@
 	{ "null-audio", 0, 0, OPT_NULL_AUDIO},
 	{ "local-port", 1, 0, OPT_LOCAL_PORT},
+	{ "ip-addr",	1, 0, OPT_IP_ADDR},
 	{ "no-tcp",     0, 0, OPT_NO_TCP},
 	{ "no-udp",     0, 0, OPT_NO_UDP},
@@ -434,4 +440,9 @@
 	    break;
 
+	case OPT_IP_ADDR: /* ip-addr */
+	    cfg->udp_cfg.public_addr = pj_str(pj_optarg);
+	    cfg->rtp_cfg.public_addr = pj_str(pj_optarg);
+	    break;
+
 	case OPT_NO_UDP: /* no-udp */
 	    if (cfg->no_tcp) {
@@ -917,4 +928,12 @@
     pj_ansi_sprintf(line, "--local-port %d\n", config->udp_cfg.port);
     pj_strcat2(&cfg, line);
+
+    /* IP address, if any. */
+    if (config->udp_cfg.public_addr.slen) {
+	pj_ansi_sprintf(line, "--ip-addr %.*s\n", 
+			(int)config->udp_cfg.public_addr.slen,
+			config->udp_cfg.public_addr.ptr);
+	pj_strcat2(&cfg, line);
+    }
 
 
Index: /pjproject/trunk/pjsip/include/pjsip/sip_transport.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_transport.h	(revision 741)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_transport.h	(revision 742)
@@ -727,4 +727,6 @@
     PJ_DECL_LIST_MEMBER(struct pjsip_tpfactory);
 
+    char		    obj_name[PJ_MAX_OBJ_NAME];	/**< Name.	*/
+
     pj_pool_t		   *pool;	    /**< Owned memory pool.	*/
     pj_lock_t		   *lock;	    /**< Lock object.		*/
Index: /pjproject/trunk/pjsip/include/pjsip/sip_transport_tcp.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_transport_tcp.h	(revision 741)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_transport_tcp.h	(revision 742)
@@ -79,4 +79,41 @@
 
 
+
+/**
+ * A newer variant of #pjsip_tcp_transport_start(), which allows specifying
+ * the published/public address of the TCP transport.
+ *
+ * @param endpt		The SIP endpoint.
+ * @param local		Optional local address to bind, or specify the
+ *			address to bind the server socket to. Both IP 
+ *			interface address and port fields are optional.
+ *			If IP interface address is not specified, socket
+ *			will be bound to PJ_INADDR_ANY. If port is not
+ *			specified, socket will be bound to any port
+ *			selected by the operating system.
+ * @param a_name	Optional published address, which is the address to be
+ *			advertised as the address of this SIP transport. 
+ *			If this argument is NULL, then the bound address
+ *			will be used as the published address.
+ * @param async_cnt	Number of simultaneous asynchronous accept()
+ *			operations to be supported. It is recommended that
+ *			the number here corresponds to the number of
+ *			processors in the system (or the number of SIP
+ *			worker threads).
+ * @param p_factory	Optional pointer to receive the instance of the
+ *			SIP TCP transport factory just created.
+ *
+ * @return		PJ_SUCCESS when the transport has been successfully
+ *			started and registered to transport manager, or
+ *			the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt,
+					        const pj_sockaddr_in *local,
+					        const pjsip_host_port *a_name,
+					        unsigned async_cnt,
+					        pjsip_tpfactory **p_factory);
+
+
+
 PJ_END_DECL
 
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 741)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 742)
@@ -725,5 +725,5 @@
 
 /**
- * Transport configuration for creating UDP transports for both SIP
+ * Transport configuration for creating transports for both SIP
  * and media.
  */
@@ -739,7 +739,26 @@
 
     /**
-     * Optional address where the socket should be bound.
-     */
-    pj_in_addr		ip_addr;
+     * Optional address to advertise as the address of this transport.
+     * Application can specify any address or hostname for this field,
+     * for example it can point to one of the interface address in the
+     * system, or it can point to the public address of a NAT router
+     * where port mappings have been configured for the application.
+     *
+     * Note: this option can be used for both UDP and TCP as well!
+     */
+    pj_str_t		public_addr;
+
+    /**
+     * Optional address where the socket should be bound to. This option
+     * SHOULD only be used to selectively bind the socket to particular
+     * interface (instead of 0.0.0.0), and SHOULD NOT be used to set the
+     * published address of a transport (the public_addr field should be
+     * used for that purpose).
+     *
+     * Note that unlike public_addr field, the address (or hostname) here 
+     * MUST correspond to the actual interface address in the host, since
+     * this address will be specified as bind() argument.
+     */
+    pj_str_t		bound_addr;
 
     /**
Index: /pjproject/trunk/pjsip/src/pjsip/sip_transport_tcp.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_transport_tcp.c	(revision 741)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_transport_tcp.c	(revision 742)
@@ -70,5 +70,4 @@
 {
     pjsip_tpfactory	     factory;
-    char		     obj_name[PJ_MAX_OBJ_NAME];
     pj_bool_t		     is_registered;
     pjsip_endpoint	    *endpt;
@@ -185,6 +184,7 @@
  * TCP listener.
  */
-PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
+PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt,
 					       const pj_sockaddr_in *local,
+					       const pjsip_host_port *a_name,
 					       unsigned async_cnt,
 					       pjsip_tpfactory **p_factory)
@@ -201,4 +201,17 @@
     PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL);
 
+    /* Verify that address given in a_name (if any) is valid */
+    if (a_name && a_name->host.slen) {
+	pj_sockaddr_in tmp;
+
+	status = pj_sockaddr_in_init(&tmp, &a_name->host, 
+				     (pj_uint16_t)a_name->port);
+	if (status != PJ_SUCCESS || tmp.sin_addr.s_addr == PJ_INADDR_ANY ||
+	    tmp.sin_addr.s_addr == PJ_INADDR_NONE)
+	{
+	    /* Invalid address */
+	    return PJ_EINVAL;
+	}
+    }
 
     pool = pjsip_endpt_create_pool(endpt, "tcplis", POOL_LIS_INIT, 
@@ -215,5 +228,5 @@
     listener->sock = PJ_INVALID_SOCKET;
 
-    pj_ansi_strcpy(listener->obj_name, "tcp");
+    pj_ansi_strcpy(listener->factory.obj_name, "tcplis");
 
     status = pj_lock_create_recursive_mutex(pool, "tcplis", 
@@ -246,23 +259,44 @@
 	goto on_error;
 
-    /* If the address returns 0.0.0.0, use the first interface address
-     * as the transport's address.
+    /* If published host/IP is specified, then use that address as the
+     * listener advertised address.
      */
-    if (listener_addr->sin_addr.s_addr == 0) {
-	pj_in_addr hostip;
-
-	status = pj_gethostip(&hostip);
-	if (status != PJ_SUCCESS)
-	    goto on_error;
-
-	listener_addr->sin_addr = hostip;
-    }
-
-    pj_ansi_snprintf(listener->obj_name, sizeof(listener->obj_name), 
-		     "tcp:%d",  (int)pj_ntohs(listener_addr->sin_port));
-
-    /* Save the address name */
-    sockaddr_to_host_port(listener->factory.pool, 
-			  &listener->factory.addr_name, listener_addr);
+    if (a_name && a_name->host.slen) {
+	/* Copy the address */
+	listener->factory.addr_name = *a_name;
+	pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, 
+		  &a_name->host);
+	listener->factory.addr_name.port = a_name->port;
+
+    } else {
+	/* No published address is given, use the bound address */
+
+	/* If the address returns 0.0.0.0, use the default
+	 * interface address as the transport's address.
+	 */
+	if (listener_addr->sin_addr.s_addr == 0) {
+	    pj_in_addr hostip;
+
+	    status = pj_gethostip(&hostip);
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+
+	    listener_addr->sin_addr = hostip;
+	}
+
+	/* Save the address name */
+	sockaddr_to_host_port(listener->factory.pool, 
+			      &listener->factory.addr_name, listener_addr);
+    }
+
+    /* If port is zero, get the bound port */
+    if (listener->factory.addr_name.port == 0) {
+	listener->factory.addr_name.port = pj_ntohs(listener_addr->sin_port);
+    }
+
+    pj_ansi_snprintf(listener->factory.obj_name, 
+		     sizeof(listener->factory.obj_name),
+		     "tcplis:%d",  listener->factory.addr_name.port);
+
 
     /* Start listening to the address */
@@ -320,8 +354,9 @@
     }
 
-    PJ_LOG(4,(listener->obj_name, 
-	     "SIP TCP listener ready for incoming connections at %s:%d",
-	     pj_inet_ntoa(listener_addr->sin_addr), 
-	     (int)pj_ntohs(listener_addr->sin_port)));
+    PJ_LOG(4,(listener->factory.obj_name, 
+	     "SIP TCP listener ready for incoming connections at %.*s:%d",
+	     (int)listener->factory.addr_name.host.slen,
+	     listener->factory.addr_name.host.ptr,
+	     listener->factory.addr_name.port));
 
     /* Return the pointer to user */
@@ -333,4 +368,17 @@
     lis_destroy(&listener->factory);
     return status;
+}
+
+
+/*
+ * This is the public API to create, initialize, register, and start the
+ * TCP listener.
+ */
+PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
+					       const pj_sockaddr_in *local,
+					       unsigned async_cnt,
+					       pjsip_tpfactory **p_factory)
+{
+    return pjsip_tcp_transport_start2(endpt, local, NULL, async_cnt, p_factory);
 }
 
@@ -374,5 +422,5 @@
 	pj_pool_t *pool = listener->factory.pool;
 
-	PJ_LOG(4,(listener->obj_name,  "SIP TCP listener destroyed"));
+	PJ_LOG(4,(listener->factory.obj_name,  "SIP TCP listener destroyed"));
 
 	listener->factory.pool = NULL;
@@ -856,5 +904,6 @@
 	     * Error in accept().
 	     */
-	    tcp_perror(listener->obj_name, "Error in accept()", status);
+	    tcp_perror(listener->factory.obj_name, "Error in accept()", 
+		       status);
 
 	    /*
@@ -866,5 +915,5 @@
 	    ++err_cnt;
 	    if (err_cnt >= 10) {
-		PJ_LOG(1, (listener->obj_name, 
+		PJ_LOG(1, (listener->factory.obj_name, 
 			   "Too many errors, listener stopping"));
 	    }
@@ -883,5 +932,5 @@
 	    }
 
-	    PJ_LOG(4,(listener->obj_name, 
+	    PJ_LOG(4,(listener->factory.obj_name, 
 		      "TCP listener %.*s:%d: got incoming TCP connection "
 		      "from %s:%d, sock=%d",
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 741)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 742)
@@ -799,4 +799,7 @@
      */
     if (stun.stun_srv1.slen) {
+	/*
+	 * STUN is specified, resolve the address with STUN.
+	 */
 	status = pj_stun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
 				         &stun.stun_srv1, 
@@ -811,4 +814,11 @@
 	}
 
+    } else if (p_pub_addr->sin_addr.s_addr != 0) {
+	/*
+	 * Public address is already specified, no need to resolve the 
+	 * address, only set the port.
+	 */
+	/* Do nothing */
+
     } else {
 
@@ -867,4 +877,5 @@
 	pjsua_transport_config config;
 	pj_sock_t sock = PJ_INVALID_SOCKET;
+	pj_sockaddr_in bound_addr;
 	pj_sockaddr_in pub_addr;
 	pjsip_host_port addr_name;
@@ -876,7 +887,34 @@
 	}
 
-	/* Create the socket and possibly resolve the address with STUN */
-	status = create_sip_udp_sock(cfg->ip_addr, cfg->port, cfg->use_stun,
-				     &cfg->stun_config, &sock, &pub_addr);
+	/* Initialize bound address, if any */
+	bound_addr.sin_addr.s_addr = PJ_INADDR_ANY;
+	if (cfg->bound_addr.slen) {
+	    status = pj_sockaddr_in_set_str_addr(&bound_addr,&cfg->bound_addr);
+	    if (status != PJ_SUCCESS) {
+		pjsua_perror(THIS_FILE, 
+			     "Unable to resolve transport bound address", 
+			     status);
+		goto on_return;
+	    }
+	}
+
+	/* Initialize the public address from the config, if any */
+	pj_sockaddr_in_init(&pub_addr, NULL, (pj_uint16_t)cfg->port);
+	if (cfg->public_addr.slen) {
+	    status = pj_sockaddr_in_set_str_addr(&pub_addr, &cfg->public_addr);
+	    if (status != PJ_SUCCESS) {
+		pjsua_perror(THIS_FILE, 
+			     "Unable to resolve transport public address", 
+			     status);
+		goto on_return;
+	    }
+	}
+
+	/* Create the socket and possibly resolve the address with STUN 
+	 * (only when public address is not specified).
+	 */
+	status = create_sip_udp_sock(bound_addr.sin_addr, cfg->port, 
+				     cfg->use_stun, &cfg->stun_config, 
+				     &sock, &pub_addr);
 	if (status != PJ_SUCCESS)
 	    goto on_return;
@@ -907,4 +945,5 @@
 	 */
 	pjsua_transport_config config;
+	pjsip_host_port a_name;
 	pjsip_tpfactory *tcp;
 	pj_sockaddr_in local_addr;
@@ -922,10 +961,22 @@
 	    local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
 
-	if (cfg->ip_addr.s_addr)
-	    local_addr.sin_addr.s_addr = cfg->ip_addr.s_addr;
+	if (cfg->bound_addr.slen) {
+	    status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
+	    if (status != PJ_SUCCESS) {
+		pjsua_perror(THIS_FILE, 
+			     "Unable to resolve transport bound address", 
+			     status);
+		goto on_return;
+	    }
+	}
+
+	/* Init published name */
+	pj_bzero(&a_name, sizeof(pjsip_host_port));
+	if (cfg->public_addr.slen)
+	    a_name.host = cfg->public_addr;
 
 	/* Create the TCP transport */
-	status = pjsip_tcp_transport_start(pjsua_var.endpt, &local_addr, 1,
-					   &tcp);
+	status = pjsip_tcp_transport_start2(pjsua_var.endpt, &local_addr, 
+					    &a_name, 1, &tcp);
 
 	if (status != PJ_SUCCESS) {
@@ -1030,4 +1081,5 @@
 {
     struct transport_data *t = &pjsua_var.tpdata[id];
+    pj_status_t status;
 
     pj_bzero(info, sizeof(*info));
@@ -1060,4 +1112,6 @@
 	info->usage_count = pj_atomic_get(tp->ref_cnt);
 
+	status = PJ_SUCCESS;
+
     } else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
 
@@ -1079,4 +1133,9 @@
 	info->usage_count = 0;
 
+	status = PJ_SUCCESS;
+
+    } else {
+	pj_assert(!"Unsupported transport");
+	status = PJ_EINVALIDOP;
     }
 
@@ -1084,5 +1143,5 @@
     PJSUA_UNLOCK();
 
-    return PJ_EINVALIDOP;
+    return status;
 }
 
@@ -1121,10 +1180,27 @@
     PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
 
-
-    /* To be done!! */
-    PJ_UNUSED_ARG(force);
-
-    PJ_TODO(pjsua_transport_close);
-
+    /* Note: destroy() may not work if there are objects still referencing
+     *	     the transport.
+     */
+    if (force) {
+	switch (pjsua_var.tpdata[id].type) {
+	case PJSIP_TRANSPORT_UDP:
+	    return pjsip_transport_destroy(pjsua_var.tpdata[id].data.tp);
+	case PJSIP_TRANSPORT_TCP:
+	    break;
+	}
+	
+    } else {
+	switch (pjsua_var.tpdata[id].type) {
+	case PJSIP_TRANSPORT_UDP:
+	    return pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
+	case PJSIP_TRANSPORT_TCP:
+	    return (*pjsua_var.tpdata[id].data.factory->destroy)
+			(pjsua_var.tpdata[id].data.factory);
+	}
+    }
+
+    /* Unreachable */
+    pj_assert(!"Unknown transport");
     return PJ_EINVALIDOP;
 }
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 741)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 742)
@@ -210,4 +210,5 @@
     int i;
     static pj_uint16_t rtp_port;
+    pj_sockaddr_in bound_addr;
     pj_sockaddr_in mapped_addr[2];
     pj_status_t status = PJ_SUCCESS;
@@ -220,4 +221,13 @@
 	sock[i] = PJ_INVALID_SOCKET;
 
+    bound_addr.sin_addr.s_addr = PJ_INADDR_ANY;
+    if (cfg->bound_addr.slen) {
+	status = pj_sockaddr_in_set_str_addr(&bound_addr, &cfg->bound_addr);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Unable to resolve transport bind address",
+			 status);
+	    return status;
+	}
+    }
 
     /* Loop retry to bind RTP and RTCP sockets. */
@@ -231,5 +241,6 @@
 	}
 
-	status = pj_sock_bind_in(sock[0], cfg->ip_addr.s_addr, rtp_port);
+	status = pj_sock_bind_in(sock[0], bound_addr.sin_addr.s_addr, 
+				 rtp_port);
 	if (status != PJ_SUCCESS) {
 	    pj_sock_close(sock[0]); 
@@ -246,5 +257,5 @@
 	}
 
-	status = pj_sock_bind_in(sock[1], cfg->ip_addr.s_addr, 
+	status = pj_sock_bind_in(sock[1], bound_addr.sin_addr.s_addr, 
 				 (pj_uint16_t)(rtp_port+1));
 	if (status != PJ_SUCCESS) {
@@ -286,4 +297,18 @@
 	    sock[1] = PJ_INVALID_SOCKET;
 
+	} else if (cfg->public_addr.slen) {
+
+	    status = pj_sockaddr_in_init(&mapped_addr[0], &cfg->public_addr,
+					 (pj_uint16_t)rtp_port);
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+
+	    status = pj_sockaddr_in_init(&mapped_addr[1], &cfg->public_addr,
+					 (pj_uint16_t)(rtp_port+1));
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+
+	    break;
+
 	} else {
 	    pj_in_addr addr;
