session UPDATE add call home local binding support
diff --git a/src/session_client.c b/src/session_client.c
index 3a09d38..a7ed968 100644
--- a/src/session_client.c
+++ b/src/session_client.c
@@ -1593,9 +1593,10 @@
  * @return Connected socket or -1 on error.
  */
 static int
-sock_connect(int timeout_ms, int *sock_pending, struct addrinfo *res, const struct nc_keepalives *ka)
+sock_connect(const char *src_addr, uint16_t src_port, int timeout_ms, int *sock_pending, struct addrinfo *res,
+        const struct nc_keepalives *ka)
 {
-    int flags, ret, error;
+    int flags, ret, error, opt;
     int sock = -1;
     struct pollfd fds = {0};
     socklen_t len = sizeof(int);
@@ -1624,6 +1625,21 @@
             ERR(NULL, "fcntl() failed (%s).", strerror(errno));
             goto cleanup;
         }
+
+        /* bind the socket to a specific address/port to make the connection from (CH only) */
+        if (src_addr || src_port) {
+            /* enable address reuse, so that we're able to bind this address again when the CH conn is dropped and retried */
+            opt = 1;
+            if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
+                ERR(NULL, "Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
+                goto cleanup;
+            }
+
+            if (nc_sock_bind_inet(sock, src_addr, src_port, (res->ai_family == AF_INET) ? 1 : 0)) {
+                goto cleanup;
+            }
+        }
+
         /* non-blocking connect! */
         if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
             if (errno != EINPROGRESS) {
@@ -1688,35 +1704,36 @@
 }
 
 int
-nc_sock_connect(const char *host, uint16_t port, int timeout_ms, struct nc_keepalives *ka, int *sock_pending, char **ip_host)
+nc_sock_connect(const char *src_addr, uint16_t src_port, const char *dst_addr, uint16_t dst_port, int timeout_ms,
+        struct nc_keepalives *ka, int *sock_pending, char **ip_host)
 {
     int i, opt;
     int sock = sock_pending ? *sock_pending : -1;
     struct addrinfo hints, *res_list = NULL, *res;
-    char port_s[6]; /* length of string representation of short int */
+    char dst_port_str[6]; /* length of string representation of short int */
     struct sockaddr_storage saddr;
     socklen_t addr_len = sizeof saddr;
 
     *ip_host = NULL;
 
-    DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout_ms, sock);
+    DBG(NULL, "nc_sock_connect(%s, %u, %s, %u, %d, %d)", src_addr, src_port, dst_addr, dst_port, timeout_ms, sock);
 
     /* no pending socket */
     if (sock == -1) {
         /* connect to a server */
-        snprintf(port_s, 6, "%u", port);
+        snprintf(dst_port_str, 6, "%u", dst_port);
         memset(&hints, 0, sizeof hints);
         hints.ai_family = AF_UNSPEC;
         hints.ai_socktype = SOCK_STREAM;
         hints.ai_protocol = IPPROTO_TCP;
-        i = getaddrinfo(host, port_s, &hints, &res_list);
+        i = getaddrinfo(dst_addr, dst_port_str, &hints, &res_list);
         if (i != 0) {
             ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
             goto error;
         }
 
         for (res = res_list; res != NULL; res = res->ai_next) {
-            sock = sock_connect(timeout_ms, sock_pending, res, ka);
+            sock = sock_connect(src_addr, src_port, timeout_ms, sock_pending, res, ka);
             if (sock == -1) {
                 if (!sock_pending || (*sock_pending == -1)) {
                     /* try the next resource */
@@ -1726,7 +1743,12 @@
                     break;
                 }
             }
-            VRB(NULL, "Successfully connected to %s:%s over %s.", host, port_s, (res->ai_family == AF_INET6) ? "IPv6" : "IPv4");
+
+            if (res->ai_family == AF_INET) {
+                VRB(NULL, "Successfully connected to %s:%s over IPv4.", dst_addr, dst_port_str);
+            } else {
+                VRB(NULL, "Successfully connected to [%s]:%s over IPv6.", dst_addr, dst_port_str);
+            }
 
             opt = 1;
             if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
@@ -1744,7 +1766,7 @@
     } else {
         /* try to get a connection with the pending socket */
         assert(sock_pending);
-        sock = sock_connect(timeout_ms, sock_pending, NULL, ka);
+        sock = sock_connect(src_addr, src_port, timeout_ms, sock_pending, NULL, ka);
 
         if (sock > 0) {
             if (getpeername(sock, (struct sockaddr *)&saddr, &addr_len)) {
diff --git a/src/session_client_ssh.c b/src/session_client_ssh.c
index 3116b89..bc08e63 100644
--- a/src/session_client_ssh.c
+++ b/src/session_client_ssh.c
@@ -1606,7 +1606,7 @@
         }
 
         /* create and connect socket */
-        sock = nc_sock_connect(host, port, -1, ka, NULL, &ip_host);
+        sock = nc_sock_connect(NULL, 0, host, port, -1, ka, NULL, &ip_host);
         if (sock == -1) {
             ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
             free(host);
@@ -1762,7 +1762,7 @@
     }
 
     /* create and assign communication socket */
-    sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
+    sock = nc_sock_connect(NULL, 0, host, port, -1, &client_opts.ka, NULL, &ip_host);
     if (sock == -1) {
         ERR(session, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
         goto fail;
diff --git a/src/session_client_tls.c b/src/session_client_tls.c
index e1fef52..722dd0a 100644
--- a/src/session_client_tls.c
+++ b/src/session_client_tls.c
@@ -390,7 +390,7 @@
     session->status = NC_STATUS_STARTING;
 
     /* create and assign socket */
-    sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
+    sock = nc_sock_connect(NULL, 0, host, port, -1, &client_opts.ka, NULL, &ip_host);
     if (sock == -1) {
         ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
         goto fail;
diff --git a/src/session_p.h b/src/session_p.h
index 32f6ea5..ce55492 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -470,8 +470,12 @@
             char *referenced_endpt_name;
 #endif /* NC_ENABLED_SSH_TLS */
             NC_TRANSPORT_IMPL ti;
-            char *address;
-            uint16_t port;
+
+            char *src_addr;                             /**< IP address to bind to when connecting to a Call Home client. */
+            uint16_t src_port;                          /**< Port to bind to when connecting to a Call Home client. */
+            char *dst_addr;                             /**< IP address of the Call Home client. */
+            uint16_t dst_port;                          /**< Port of the Call Home client. */
+
             int sock_pending;
             struct nc_keepalives ka;
 
@@ -897,10 +901,23 @@
 NC_MSG_TYPE nc_handshake_io(struct nc_session *session);
 
 /**
+ * @brief Bind a socket to an address and a port.
+ *
+ * @param[in] sock Socket to bind.
+ * @param[in] address Address to bind to.
+ * @param[in] port Port to bind to.
+ * @param[in] is_ipv4 Whether the address is IPv4 or IPv6.
+ * @return 0 on success, -1 on error.
+ */
+int nc_sock_bind_inet(int sock, const char *address, uint16_t port, int is_ipv4);
+
+/**
  * @brief Create a socket connection.
  *
- * @param[in] host Hostname to connect to.
- * @param[in] port Port to connect on.
+ * @param[in] src_addr Address to connect from.
+ * @param[in] src_port Port to connect from.
+ * @param[in] dst_addr Address to connect to.
+ * @param[in] dst_port Port to connect to.
  * @param[in] timeout_ms Timeout in ms for blocking the connect + select call (-1 for infinite).
  * @param[in] ka Keepalives parameters.
  * @param[in,out] sock_pending Previous pending socket. If set, equal to -1, and the connection is still in progress
@@ -908,8 +925,8 @@
  * @param[out] ip_host Optional parameter with string IP address of the connected host.
  * @return Connected socket or -1 on error.
  */
-int nc_sock_connect(const char *host, uint16_t port, int timeout_ms, struct nc_keepalives *ka, int *sock_pending,
-        char **ip_host);
+int nc_sock_connect(const char *src_addr, uint16_t src_port, const char *dst_addr, uint16_t dst_port, int timeout_ms,
+        struct nc_keepalives *ka, int *sock_pending, char **ip_host);
 
 /**
  * @brief Accept a new socket connection.
diff --git a/src/session_server.c b/src/session_server.c
index 14bc410..63906ec 100644
--- a/src/session_server.c
+++ b/src/session_server.c
@@ -272,14 +272,65 @@
 #endif
 
 int
+nc_sock_bind_inet(int sock, const char *address, uint16_t port, int is_ipv4)
+{
+    struct sockaddr_storage saddr;
+    struct sockaddr_in *saddr4;
+    struct sockaddr_in6 *saddr6;
+
+    memset(&saddr, 0, sizeof(struct sockaddr_storage));
+
+    if (is_ipv4) {
+        saddr4 = (struct sockaddr_in *)&saddr;
+
+        saddr4->sin_family = AF_INET;
+        saddr4->sin_port = htons(port);
+
+        /* determine the address */
+        if (!address) {
+            /* set the implicit default IPv4 address */
+            address = "0.0.0.0";
+        }
+        if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
+            ERR(NULL, "Failed to convert IPv4 address \"%s\".", address);
+            return -1;
+        }
+
+        if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
+            ERR(NULL, "Could not bind %s:%" PRIu16 " (%s).", address, port, strerror(errno));
+            return -1;
+        }
+
+    } else {
+        saddr6 = (struct sockaddr_in6 *)&saddr;
+
+        saddr6->sin6_family = AF_INET6;
+        saddr6->sin6_port = htons(port);
+
+        /* determine the address */
+        if (!address) {
+            /* set the implicit default IPv6 address */
+            address = "::";
+        }
+        if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
+            ERR(NULL, "Failed to convert IPv6 address \"%s\".", address);
+            return -1;
+        }
+
+        if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
+            ERR(NULL, "Could not bind [%s]:%" PRIu16 " (%s).", address, port, strerror(errno));
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+int
 nc_sock_listen_inet(const char *address, uint16_t port)
 {
     int opt;
     int is_ipv4, sock;
-    struct sockaddr_storage saddr;
-
-    struct sockaddr_in *saddr4;
-    struct sockaddr_in6 *saddr6;
 
     if (!strchr(address, ':')) {
         is_ipv4 = 1;
@@ -304,38 +355,9 @@
         goto fail;
     }
 
-    memset(&saddr, 0, sizeof(struct sockaddr_storage));
-    if (is_ipv4) {
-        saddr4 = (struct sockaddr_in *)&saddr;
-
-        saddr4->sin_family = AF_INET;
-        saddr4->sin_port = htons(port);
-
-        if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
-            ERR(NULL, "Failed to convert IPv4 address \"%s\".", address);
-            goto fail;
-        }
-
-        if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
-            ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
-            goto fail;
-        }
-
-    } else {
-        saddr6 = (struct sockaddr_in6 *)&saddr;
-
-        saddr6->sin6_family = AF_INET6;
-        saddr6->sin6_port = htons(port);
-
-        if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
-            ERR(NULL, "Failed to convert IPv6 address \"%s\".", address);
-            goto fail;
-        }
-
-        if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
-            ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
-            goto fail;
-        }
+    /* bind the socket */
+    if (nc_sock_bind_inet(sock, address, port, is_ipv4)) {
+        goto fail;
     }
 
     if (listen(sock, NC_REVERSE_QUEUE) == -1) {
@@ -2491,7 +2513,8 @@
     struct timespec ts_cur;
     char *ip_host;
 
-    sock = nc_sock_connect(endpt->address, endpt->port, NC_CH_CONNECT_TIMEOUT, &endpt->ka, &endpt->sock_pending, &ip_host);
+    sock = nc_sock_connect(endpt->src_addr, endpt->src_port, endpt->dst_addr, endpt->dst_port,
+            NC_CH_CONNECT_TIMEOUT, &endpt->ka, &endpt->sock_pending, &ip_host);
     if (sock < 0) {
         return NC_MSG_ERROR;
     }
@@ -2515,7 +2538,7 @@
     (*session)->ctx = (struct ly_ctx *)ctx;
     (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
     (*session)->host = ip_host;
-    (*session)->port = endpt->port;
+    (*session)->port = endpt->dst_port;
 
     /* sock gets assigned to session or closed */
     if (endpt->ti == NC_TI_SSH) {