Skip to content

Commit 25b9bb1

Browse files
committed
make a pull request for issue 1188
1 parent fdc7478 commit 25b9bb1

5 files changed

Lines changed: 114 additions & 45 deletions

File tree

src/session_client.c

Lines changed: 82 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -898,65 +898,107 @@ nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx)
898898
return NULL;
899899
}
900900

901+
/* A given timeout value limits the time how long the function blocks. If it has to block
902+
only for some seconds, a socket connection might not yet have been fully established.
903+
Therefore the active (pending) socket will be stored in *sock_pending, but the return
904+
value will be -1. In such a case a subsequent invokation is required, by providing the
905+
stored sock_pending, again.
906+
In general, if this function returns -1, when a timeout has been given, this function
907+
has to be invoked, until it returns a valid socket.
908+
*/
901909
int
902-
nc_sock_connect(const char* host, uint16_t port)
910+
nc_sock_connect(const char* host, uint16_t port, int timeout, int* sock_pending)
903911
{
904-
int i, sock = -1, flags;
912+
int i, flags, ret=0;
913+
int sock = sock_pending?*sock_pending:-1;
914+
fd_set wset;
905915
struct addrinfo hints, *res_list, *res;
906916
char port_s[6]; /* length of string representation of short int */
917+
struct timeval ts;
907918

908-
snprintf(port_s, 6, "%u", port);
919+
ts.tv_sec = timeout;
920+
ts.tv_usec = 0;
909921

910-
/* Connect to a server */
911-
memset(&hints, 0, sizeof hints);
912-
hints.ai_family = AF_UNSPEC;
913-
hints.ai_socktype = SOCK_STREAM;
914-
hints.ai_protocol = IPPROTO_TCP;
915-
i = getaddrinfo(host, port_s, &hints, &res_list);
916-
if (i != 0) {
917-
ERR("Unable to translate the host address (%s).", gai_strerror(i));
918-
return -1;
919-
}
922+
VRB("nc_sock_connect(%s, %u, %d, %d)", host, port, timeout, sock);
920923

921-
for (res = res_list; res != NULL; res = res->ai_next) {
922-
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
923-
if (sock == -1) {
924-
/* socket was not created, try another resource */
925-
continue;
924+
/* no pending socket */
925+
if (sock == -1) {
926+
/* Connect to a server */
927+
snprintf(port_s, 6, "%u", port);
928+
memset(&hints, 0, sizeof hints);
929+
hints.ai_family = AF_UNSPEC;
930+
hints.ai_socktype = SOCK_STREAM;
931+
hints.ai_protocol = IPPROTO_TCP;
932+
i = getaddrinfo(host, port_s, &hints, &res_list);
933+
if (i != 0) {
934+
ERR("Unable to translate the host address (%s).", gai_strerror(i));
935+
return -1;
926936
}
927937

928-
if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
929-
/* network connection failed, try another resource */
930-
close(sock);
931-
sock = -1;
932-
continue;
938+
for (res = res_list; res != NULL; res = res->ai_next) {
939+
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
940+
if (sock == -1) {
941+
/* socket was not created, try another resource */
942+
continue;
943+
}
944+
/* make the socket non-blocking */
945+
if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
946+
ERR("Fcntl failed (%s).", strerror(errno));
947+
close(sock);
948+
freeaddrinfo(res_list);
949+
return -1;
950+
}
951+
/* enable keep-alive */
952+
i = 1;
953+
if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &i, sizeof i) == -1) {
954+
ERR("Setsockopt failed (%s).", strerror(errno));
955+
close(sock);
956+
freeaddrinfo(res_list);
957+
return -1;
958+
}
959+
/* non-blocking connect! */
960+
if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
961+
if (errno != EINPROGRESS) {
962+
/* network connection failed, try another resource */
963+
VRB("connect failed: (%s).", strerror(errno));
964+
close(sock);
965+
sock = -1;
966+
continue;
967+
}
968+
}
933969
}
970+
freeaddrinfo(res_list);
971+
}
972+
/* new socket or pending socket */
973+
if (sock != -1) {
934974

935-
/* make the socket non-blocking */
936-
if (((flags = fcntl(sock, F_GETFL)) == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
937-
ERR("Fcntl failed (%s).", strerror(errno));
975+
FD_ZERO(&wset);
976+
FD_SET(sock, &wset);
977+
978+
if ((ret = select(sock + 1, NULL, &wset, NULL, (timeout != -1) ? &ts : NULL)) < 0) {
979+
ERR("select failed: (%s).", strerror(errno));
938980
close(sock);
939-
freeaddrinfo(res_list);
940981
return -1;
941982
}
942983

943-
/* enable keep-alive */
944-
i = 1;
945-
if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &i, sizeof i) == -1) {
946-
ERR("Setsockopt failed (%s).", strerror(errno));
947-
close(sock);
948-
freeaddrinfo(res_list);
984+
if (ret == 0) { //we had a timeout
985+
VRB("timed out after %ds (%s).", timeout, strerror(errno));
986+
/* in that case we need to store it as pending for another attempt */
987+
if (sock_pending) {
988+
*sock_pending = sock;
989+
} else {
990+
close(sock);
991+
}
949992
return -1;
950993
}
951994

952-
/* we're done, network connection established */
953-
break;
954-
}
955-
956-
if (sock != -1) {
957-
VRB("Successfully connected to %s:%s over %s.", host, port_s, (res->ai_family == AF_INET6) ? "IPv6" : "IPv4");
995+
if (!FD_ISSET(sock, &wset)) {
996+
ERR("FD_ISSET failed: (%s).", strerror(errno));
997+
close(sock);
998+
return -1;
999+
}
1000+
VRB("Successfully connected to %s:%s.", host, port_s);
9581001
}
959-
freeaddrinfo(res_list);
9601002

9611003
return sock;
9621004
}

src/session_client_ssh.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,7 +1514,7 @@ _nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_client
15141514
ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
15151515

15161516
/* create and connect socket */
1517-
sock = nc_sock_connect(host, port);
1517+
sock = nc_sock_connect(host, port, -1, NULL);
15181518
if (sock == -1) {
15191519
ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
15201520
goto fail;
@@ -1663,7 +1663,7 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
16631663
}
16641664

16651665
/* create and assign communication socket */
1666-
sock = nc_sock_connect(host, port);
1666+
sock = nc_sock_connect(host, port, -1, NULL);
16671667
if (sock == -1) {
16681668
ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
16691669
goto fail;

src/session_client_tls.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
617617
}
618618

619619
/* create and assign socket */
620-
sock = nc_sock_connect(host, port);
620+
sock = nc_sock_connect(host, port, -1, NULL);
621621
if (sock == -1) {
622622
ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
623623
goto fail;

src/session_p.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ struct nc_server_opts {
230230
const char *name;
231231
const char *address;
232232
uint16_t port;
233+
int sock_pending;
233234
} *ch_endpts;
234235
uint16_t ch_endpt_count;
235236
union {
@@ -516,9 +517,11 @@ NC_MSG_TYPE nc_handshake_io(struct nc_session *session);
516517
*
517518
* @param[in] host Hostname to connect to.
518519
* @param[in] port Port to connect on.
520+
* @param[in] timeout for blocking the connect+select call (-1 for infinite).
521+
* @param[in] sock_pending for exchanging the pending socket, if the blocking timeout was != -1
519522
* @return Connected socket or -1 on error.
520523
*/
521-
int nc_sock_connect(const char *host, uint16_t port);
524+
int nc_sock_connect(const char *host, uint16_t port, int timeout, int* sock_pending);
522525

523526
/**
524527
* @brief Accept a new socket connection.

src/session_server.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ API int
467467
nc_server_init(struct ly_ctx *ctx)
468468
{
469469
const struct lys_node *rpc;
470+
pthread_rwlockattr_t attr;
470471

471472
if (!ctx) {
472473
ERRARG("ctx");
@@ -492,6 +493,23 @@ nc_server_init(struct ly_ctx *ctx)
492493
server_opts.new_session_id = 1;
493494
pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
494495

496+
errno=0;
497+
498+
if (pthread_rwlockattr_init(&attr) == 0) {
499+
if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
500+
if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
501+
ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
502+
}
503+
if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
504+
ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
505+
}
506+
} else {
507+
ERR("%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
508+
}
509+
pthread_rwlockattr_destroy(&attr);
510+
} else {
511+
ERR("%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
512+
}
495513
return 0;
496514
}
497515

@@ -2239,6 +2257,7 @@ nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
22392257
client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
22402258
client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
22412259
client->ch_endpts[client->ch_endpt_count - 1].port = 0;
2260+
client->ch_endpts[client->ch_endpt_count - 1].sock_pending = -1;
22422261

22432262
/* UNLOCK */
22442263
nc_server_ch_client_unlock(client);
@@ -2269,6 +2288,9 @@ nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
22692288
for (i = 0; i < client->ch_endpt_count; ++i) {
22702289
lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
22712290
lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2291+
if (client->ch_endpts[i].sock_pending != -1) {
2292+
close(client->ch_endpts[i].sock_pending);
2293+
}
22722294
}
22732295
free(client->ch_endpts);
22742296
client->ch_endpts = NULL;
@@ -2654,10 +2676,12 @@ nc_connect_ch_client_endpt(struct nc_ch_client *client, struct nc_ch_endpt *endp
26542676
int sock, ret;
26552677
struct timespec ts_cur;
26562678

2657-
sock = nc_sock_connect(endpt->address, endpt->port);
2679+
sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->sock_pending);
26582680
if (sock < 0) {
26592681
return NC_MSG_ERROR;
26602682
}
2683+
/* no need to store the socket as pending any longer */
2684+
endpt->sock_pending = -1;
26612685

26622686
*session = nc_new_session(NC_SERVER, 0);
26632687
if (!(*session)) {

0 commit comments

Comments
 (0)