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) {