blob: 45e669b0f840c9950ca73ce77d51ecfc5b28378b [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 server session manipulation functions
5 *
Michal Vasko18aeb5d2017-02-17 09:23:56 +01006 * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01007 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
apropp-molex4e903c32020-04-20 03:06:58 -040014#define _QNX_SOURCE /* getpeereid */
Olivier Matzac7fa2f2018-10-11 10:02:04 +020015#define _GNU_SOURCE /* signals, threads, SO_PEERCRED */
Michal Vasko086311b2016-01-08 09:53:11 +010016
Michal Vaskob83a3fa2021-05-26 09:53:42 +020017#include <arpa/inet.h>
Michal Vasko086311b2016-01-08 09:53:11 +010018#include <errno.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020019#include <fcntl.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020020#include <netinet/in.h>
21#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020022#include <poll.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010023#include <pthread.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020024#include <pwd.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020025#include <signal.h>
26#include <stdint.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/socket.h>
30#include <sys/types.h>
31#include <sys/un.h>
32#include <time.h>
33#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010034
Michal Vasko7a20d2e2021-05-19 16:40:23 +020035#include "compat.h"
Michal Vasko1a38c862016-01-15 15:50:07 +010036#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010037#include "session_server.h"
Michal Vasko0bdf70b2019-06-24 19:20:20 +020038#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010039
Michal Vaskob48aa812016-01-18 14:13:09 +010040struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010041#ifdef NC_ENABLED_SSH
42 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
43#endif
44 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020045 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
46 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010047};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010048
fanchanghu966f2de2016-07-21 02:28:57 -040049static nc_rpc_clb global_rpc_clb = NULL;
50
Michal Vasko3031aae2016-01-27 16:07:18 +010051struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010052nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010053{
54 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010055 struct nc_endpt *endpt = NULL;
56
Michal Vaskoddce1212019-05-24 09:58:49 +020057 if (!name) {
58 ERRARG("endpt_name");
59 return NULL;
60 }
61
Michal Vaskoade892d2017-02-22 13:40:35 +010062 /* WRITE LOCK */
63 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010064
65 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020066 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010067 endpt = &server_opts.endpts[i];
68 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010069 }
70 }
71
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010072 if (!endpt) {
73 ERR("Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010074 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020075 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010076 return NULL;
77 }
78
Michal Vaskoe2713da2016-08-22 16:06:40 +020079 if (idx) {
80 *idx = i;
81 }
82
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010083 return endpt;
84}
85
Michal Vaskoadf30f02019-06-24 09:34:47 +020086struct nc_ch_endpt *
87nc_server_ch_client_lock(const char *name, const char *endpt_name, NC_TRANSPORT_IMPL ti, struct nc_ch_client **client_p)
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010088{
Michal Vaskoadf30f02019-06-24 09:34:47 +020089 uint16_t i, j;
Michal Vasko2e6defd2016-10-07 15:48:15 +020090 struct nc_ch_client *client = NULL;
Michal Vaskoadf30f02019-06-24 09:34:47 +020091 struct nc_ch_endpt *endpt = NULL;
92
93 *client_p = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +020094
Michal Vaskoddce1212019-05-24 09:58:49 +020095 if (!name) {
96 ERRARG("client_name");
97 return NULL;
98 }
99
Michal Vasko2e6defd2016-10-07 15:48:15 +0200100 /* READ LOCK */
101 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
102
103 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200104 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200105 client = &server_opts.ch_clients[i];
Michal Vaskoadf30f02019-06-24 09:34:47 +0200106 if (!endpt_name && !ti) {
107 /* return only client */
108 break;
109 }
110 for (j = 0; j < client->ch_endpt_count; ++j) {
111 if (!strcmp(client->ch_endpts[j].name, endpt_name) && (!ti || (ti == client->ch_endpts[j].ti))) {
112 endpt = &client->ch_endpts[j];
113 break;
114 }
115 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200116 break;
117 }
118 }
119
120 if (!client) {
121 ERR("Call Home client \"%s\" was not found.", name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200122
Michal Vasko2e6defd2016-10-07 15:48:15 +0200123 /* READ UNLOCK */
124 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200125 } else if (endpt_name && ti && !endpt) {
126 ERR("Call Home client \"%s\" endpoint \"%s\" was not found.", name, endpt_name);
127
128 /* READ UNLOCK */
129 pthread_rwlock_unlock(&server_opts.ch_client_lock);
130 } else {
131 /* CH CLIENT LOCK */
132 pthread_mutex_lock(&client->lock);
133
134 *client_p = client;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200135 }
136
Michal Vaskoadf30f02019-06-24 09:34:47 +0200137 return endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200138}
139
140void
141nc_server_ch_client_unlock(struct nc_ch_client *client)
142{
143 /* CH CLIENT UNLOCK */
144 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100145
146 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200147 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100148}
Michal Vasko086311b2016-01-08 09:53:11 +0100149
Michal Vasko1a38c862016-01-15 15:50:07 +0100150API void
151nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
152{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200153 if (!session) {
154 ERRARG("session");
155 return;
156 } else if (!reason) {
157 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100158 return;
159 }
160
Michal Vasko142cfea2017-08-07 10:12:11 +0200161 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
162 session->killed_by = 0;
163 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100164 session->term_reason = reason;
165}
166
Michal Vasko142cfea2017-08-07 10:12:11 +0200167API void
168nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
169{
170 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
171 ERRARG("session");
172 return;
173 } else if (!sid) {
174 ERRARG("sid");
175 return;
176 }
177
178 session->killed_by = sid;
179}
180
181API void
182nc_session_set_status(struct nc_session *session, NC_STATUS status)
183{
184 if (!session) {
185 ERRARG("session");
186 return;
187 } else if (!status) {
188 ERRARG("status");
189 return;
190 }
191
192 session->status = status;
193}
194
Michal Vasko086311b2016-01-08 09:53:11 +0100195int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200196nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100197{
Michal Vasko06c860d2018-07-09 16:08:52 +0200198 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100199 int is_ipv4, sock;
200 struct sockaddr_storage saddr;
201
202 struct sockaddr_in *saddr4;
203 struct sockaddr_in6 *saddr6;
204
Michal Vasko086311b2016-01-08 09:53:11 +0100205 if (!strchr(address, ':')) {
206 is_ipv4 = 1;
207 } else {
208 is_ipv4 = 0;
209 }
210
211 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
212 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100213 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100214 goto fail;
215 }
216
Michal Vaskobe52dc22018-10-17 09:28:17 +0200217 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200218 opt = 1;
219 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
220 ERR("Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
221 goto fail;
222 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100223 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
224 ERR("Could not set TCP_NODELAY socket option (%s).", strerror(errno));
225 goto fail;
226 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200227
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200228 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100229 goto fail;
230 }
231
Michal Vaskof22d5ff2020-04-15 11:10:27 +0200232 memset(&saddr, 0, sizeof(struct sockaddr_storage));
Michal Vasko086311b2016-01-08 09:53:11 +0100233 if (is_ipv4) {
234 saddr4 = (struct sockaddr_in *)&saddr;
235
236 saddr4->sin_family = AF_INET;
237 saddr4->sin_port = htons(port);
238
239 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100240 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100241 goto fail;
242 }
243
244 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100245 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100246 goto fail;
247 }
248
249 } else {
250 saddr6 = (struct sockaddr_in6 *)&saddr;
251
252 saddr6->sin6_family = AF_INET6;
253 saddr6->sin6_port = htons(port);
254
255 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100256 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100257 goto fail;
258 }
259
260 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100261 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100262 goto fail;
263 }
264 }
265
Michal Vaskofb89d772016-01-08 12:25:35 +0100266 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100267 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100268 goto fail;
269 }
270
271 return sock;
272
273fail:
274 if (sock > -1) {
275 close(sock);
276 }
277
278 return -1;
279}
280
281int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200282nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
283{
284 struct sockaddr_un sun;
285 int sock = -1;
286
287 sock = socket(AF_UNIX, SOCK_STREAM, 0);
288 if (sock == -1) {
289 ERR("Failed to create socket (%s).", strerror(errno));
290 goto fail;
291 }
292
293 memset(&sun, 0, sizeof(sun));
294 sun.sun_family = AF_UNIX;
295 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
296
297 unlink(sun.sun_path);
298 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
299 ERR("Could not bind \"%s\" (%s).", address, strerror(errno));
300 goto fail;
301 }
302
303 if (opts->mode != (mode_t)-1) {
304 if (chmod(sun.sun_path, opts->mode) < 0) {
305 ERR("Failed to set unix socket permissions (%s).", strerror(errno));
306 goto fail;
307 }
308 }
309
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200310 if ((opts->uid != (uid_t)-1) || (opts->gid != (gid_t)-1)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200311 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
312 ERR("Failed to set unix socket uid/gid (%s).", strerror(errno));
313 goto fail;
314 }
315 }
316
317 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
318 ERR("Unable to start listening on \"%s\" (%s).", address, strerror(errno));
319 goto fail;
320 }
321
322 return sock;
323
324fail:
325 if (sock > -1) {
326 close(sock);
327 }
328
329 return -1;
330}
331
aPiecek90ff0242021-02-14 14:58:01 +0100332/**
333 * @brief Evaluate socket name for AF_UNIX socket.
334 * @param[in] acc_sock_fd is file descriptor for the accepted socket (a nonnegative).
335 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
336 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
337 * @return 0 if the stream socket is unnamed. Parameter host is set to NULL.
338 * @return -1 in case of error. Parameter host is set to NULL.
339 */
340static int
341sock_host_unix(int acc_sock_fd, char **host)
342{
343 char *sun_path;
344 struct sockaddr_storage saddr;
345 socklen_t addr_len;
346
347 *host = NULL;
348 saddr.ss_family = AF_UNIX;
349 addr_len = sizeof(saddr);
350
351 if (getsockname(acc_sock_fd, (struct sockaddr *)&saddr, &addr_len)) {
352 ERR("getsockname failed (%s).", strerror(errno));
353 return -1;
354 }
355
356 sun_path = ((struct sockaddr_un *)&saddr)->sun_path;
357 if (!sun_path) {
358 /* stream socket is unnamed */
359 return 0;
360 }
361
362 if (!(*host = strdup(sun_path))) {
363 ERRMEM;
364 return -1;
365 }
366
367 return 0;
368}
369
370/**
371 * @brief Evaluate socket name and port number for AF_INET socket.
372 * @param[in] addr is pointing to structure filled by accept function which was successful.
373 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
374 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
375 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
376 * @return -1 in case of error. Parameter host is set to NULL and port is unchanged.
377 */
378static int
379sock_host_inet(const struct sockaddr_in *addr, char **host, uint16_t *port)
380{
381 *host = malloc(INET_ADDRSTRLEN);
382 if (!(*host)) {
383 ERRMEM;
384 return -1;
385 }
386
aPiecek3da9b342021-02-18 15:00:03 +0100387 if (!inet_ntop(AF_INET, &addr->sin_addr, *host, INET_ADDRSTRLEN)) {
aPiecek90ff0242021-02-14 14:58:01 +0100388 ERR("inet_ntop failed(%s).");
389 free(*host);
390 *host = NULL;
391 return -1;
392 }
393
394 if (port) {
395 *port = ntohs(addr->sin_port);
396 }
397
398 return 0;
399}
400
401/**
402 * @brief Evaluate socket name and port number for AF_INET6 socket.
403 * @param[in] addr is pointing to structure filled by accept function which was successful.
404 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
405 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
406 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
407 * @return -1 in case of error. Parameter host is set to the NULL and port is unchanged.
408 */
409static int
410sock_host_inet6(const struct sockaddr_in6 *addr, char **host, uint16_t *port)
411{
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200412 *host = malloc(INET6_ADDRSTRLEN);
aPiecek90ff0242021-02-14 14:58:01 +0100413 if (!(*host)) {
414 ERRMEM;
415 return -1;
416 }
417
aPiecek3da9b342021-02-18 15:00:03 +0100418 if (!inet_ntop(AF_INET6, &addr->sin6_addr, *host, INET6_ADDRSTRLEN)) {
aPiecek90ff0242021-02-14 14:58:01 +0100419 ERR("inet_ntop failed(%s).");
420 free(*host);
421 *host = NULL;
422 return -1;
423 }
424
425 if (port) {
426 *port = ntohs(addr->sin6_port);
427 }
428
429 return 0;
430}
431
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200432int
Michal Vasko3031aae2016-01-27 16:07:18 +0100433nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx)
Michal Vasko086311b2016-01-08 09:53:11 +0100434{
Michal Vaskof54cd352017-02-22 13:42:02 +0100435 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100436 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100437 struct pollfd *pfd;
438 struct sockaddr_storage saddr;
439 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100440 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100441
442 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100443 if (!pfd) {
444 ERRMEM;
445 return -1;
446 }
447
Michal Vaskoac2f6182017-01-30 14:32:03 +0100448 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200449 if (binds[i].sock < 0) {
450 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200451 continue;
452 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200453 if (binds[i].pollin) {
454 binds[i].pollin = 0;
455 /* leftover pollin */
456 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100457 break;
458 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100459 pfd[pfd_count].fd = binds[i].sock;
460 pfd[pfd_count].events = POLLIN;
461 pfd[pfd_count].revents = 0;
462
463 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100464 }
465
Michal Vasko0a3f3752016-10-13 14:58:38 +0200466 if (sock == -1) {
467 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100468 sigfillset(&sigmask);
469 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100470 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100471 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
472
Michal Vasko0a3f3752016-10-13 14:58:38 +0200473 if (!ret) {
474 /* we timeouted */
475 free(pfd);
476 return 0;
477 } else if (ret == -1) {
478 ERR("Poll failed (%s).", strerror(errno));
479 free(pfd);
480 return -1;
481 }
Michal Vasko086311b2016-01-08 09:53:11 +0100482
Michal Vaskoac2f6182017-01-30 14:32:03 +0100483 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
484 /* adjust i so that indices in binds and pfd always match */
485 while (binds[i].sock != pfd[j].fd) {
486 ++i;
487 }
488
489 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200490 --ret;
491
492 if (!ret) {
493 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100494 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200495 break;
496 } else {
497 /* just remember the event for next time */
498 binds[i].pollin = 1;
499 }
500 }
Michal Vasko086311b2016-01-08 09:53:11 +0100501 }
502 }
503 free(pfd);
504
505 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100506 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100507 return -1;
508 }
509
510 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100511 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100512 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100513 return -1;
514 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200515 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100516
Michal Vasko0190bc32016-03-02 15:47:49 +0100517 /* make the socket non-blocking */
518 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
519 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100520 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100521 return -1;
522 }
523
Michal Vasko3031aae2016-01-27 16:07:18 +0100524 if (idx) {
525 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100526 }
527
aPiecek90ff0242021-02-14 14:58:01 +0100528 if (!host) {
529 return ret;
530 }
Michal Vasko086311b2016-01-08 09:53:11 +0100531
aPiecek90ff0242021-02-14 14:58:01 +0100532 if (port) {
533 *port = 0;
534 }
Michal Vasko086311b2016-01-08 09:53:11 +0100535
aPiecek90ff0242021-02-14 14:58:01 +0100536 if (saddr.ss_family == AF_UNIX) {
537 sock_host_unix(ret, host);
538 } else if (saddr.ss_family == AF_INET) {
539 sock_host_inet((struct sockaddr_in *)&saddr, host, port);
540 } else if (saddr.ss_family == AF_INET6) {
541 sock_host_inet6((struct sockaddr_in6 *)&saddr, host, port);
542 } else {
543 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100544 }
545
546 return ret;
547}
548
Michal Vasko05ba9df2016-01-13 14:40:27 +0100549static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100550nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100551{
Michal Vasko77367452021-02-16 16:32:18 +0100552 const char *identifier = NULL, *revision = NULL, *format = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100553 char *model_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100554 struct ly_out *out;
Michal Vasko9b1a9522021-03-15 16:24:26 +0100555 const struct lys_module *module = NULL, *mod;
Michal Vasko77367452021-02-16 16:32:18 +0100556 const struct lysp_submodule *submodule = NULL;
557 struct lyd_node *child, *err, *data = NULL;
558 LYS_OUTFORMAT outformat = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100559
Michal Vasko77367452021-02-16 16:32:18 +0100560 LY_LIST_FOR(lyd_child(rpc), child) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100561 if (!strcmp(child->schema->name, "identifier")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200562 identifier = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100563 } else if (!strcmp(child->schema->name, "version")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200564 revision = lyd_get_value(child);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200565 if (revision && (revision[0] == '\0')) {
Michal Vasko77367452021-02-16 16:32:18 +0100566 revision = NULL;
Radek Krejci1afa7792017-03-26 11:24:16 -0500567 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100568 } else if (!strcmp(child->schema->name, "format")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200569 format = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100570 }
571 }
Michal Vasko77367452021-02-16 16:32:18 +0100572 VRB("Schema \"%s@%s\" was requested.", identifier, revision ? revision : "<any>");
Michal Vasko05ba9df2016-01-13 14:40:27 +0100573
Michal Vasko77367452021-02-16 16:32:18 +0100574 /* check revision */
575 if (revision && (strlen(revision) != 10) && strcmp(revision, "1.0")) {
576 err = nc_err(server_opts.ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100577 nc_err_set_msg(err, "The requested version is not supported.", "en");
578 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100579 }
580
Michal Vasko77367452021-02-16 16:32:18 +0100581 if (revision) {
582 /* get specific module */
583 module = ly_ctx_get_module(server_opts.ctx, identifier, revision);
584 if (!module) {
585 submodule = ly_ctx_get_submodule(server_opts.ctx, identifier, revision);
586 }
587 } else {
588 /* try to get implemented, then latest module */
589 module = ly_ctx_get_module_implemented(server_opts.ctx, identifier);
590 if (!module) {
591 module = ly_ctx_get_module_latest(server_opts.ctx, identifier);
592 }
593 if (!module) {
594 submodule = ly_ctx_get_submodule_latest(server_opts.ctx, identifier);
595 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200596 }
Michal Vasko77367452021-02-16 16:32:18 +0100597 if (!module && !submodule) {
598 err = nc_err(server_opts.ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100599 nc_err_set_msg(err, "The requested schema was not found.", "en");
600 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100601 }
602
603 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100604 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko77367452021-02-16 16:32:18 +0100605 outformat = LYS_OUT_YANG;
Radek Krejci90fba642016-12-07 15:59:45 +0100606 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko77367452021-02-16 16:32:18 +0100607 outformat = LYS_OUT_YIN;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100608 } else {
Michal Vasko77367452021-02-16 16:32:18 +0100609 err = nc_err(server_opts.ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100610 nc_err_set_msg(err, "The requested format is not supported.", "en");
611 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100612 }
Michal Vasko77367452021-02-16 16:32:18 +0100613
614 /* print */
615 ly_out_new_memory(&model_data, 0, &out);
616 if (module) {
617 lys_print_module(out, module, outformat, 0, 0);
618 } else {
619 lys_print_submodule(out, submodule, outformat, 0, 0);
620 }
621 ly_out_free(out, NULL, 0);
Michal Vaskod91f6e62016-04-05 11:34:22 +0200622 if (!model_data) {
623 ERRINT;
624 return NULL;
625 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100626
Michal Vasko9b1a9522021-03-15 16:24:26 +0100627 /* create reply */
628 mod = ly_ctx_get_module_implemented(server_opts.ctx, "ietf-netconf-monitoring");
629 if (!mod || lyd_new_inner(NULL, mod, "get-schema", 0, &data)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100630 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200631 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100632 return NULL;
633 }
Michal Vasko9b1a9522021-03-15 16:24:26 +0100634 lydict_insert_zc(server_opts.ctx, model_data, (const char **)&model_data);
635 if (lyd_new_any(data, NULL, "data", model_data, 1, LYD_ANYDATA_STRING, 1, NULL)) {
636 ERRINT;
637 lydict_remove(server_opts.ctx, model_data);
638 lyd_free_tree(data);
639 return NULL;
640 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100641
Radek Krejci36dfdb32016-09-01 16:56:35 +0200642 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100643}
644
645static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100646nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100647{
Michal Vasko428087d2016-01-14 16:04:28 +0100648 session->term_reason = NC_SESSION_TERM_CLOSED;
649 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100650}
651
Michal Vasko086311b2016-01-08 09:53:11 +0100652API int
653nc_server_init(struct ly_ctx *ctx)
654{
Michal Vasko77367452021-02-16 16:32:18 +0100655 struct lysc_node *rpc;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000656 pthread_rwlockattr_t attr;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100657
Michal Vasko086311b2016-01-08 09:53:11 +0100658 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200659 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100660 return -1;
661 }
662
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100663 nc_init();
664
Michal Vasko05ba9df2016-01-13 14:40:27 +0100665 /* set default <get-schema> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100666 rpc = NULL;
667 if (ly_ctx_get_module_implemented(ctx, "ietf-netconf-monitoring")) {
668 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
669 }
Michal Vasko88639e92017-08-03 14:38:10 +0200670 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100671 rpc->priv = nc_clb_default_get_schema;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100672 }
673
674 /* set default <close-session> callback if not specififed */
Michal Vasko77367452021-02-16 16:32:18 +0100675 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf:close-session", 0);
Michal Vasko88639e92017-08-03 14:38:10 +0200676 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100677 rpc->priv = nc_clb_default_close_session;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100678 }
679
Michal Vasko086311b2016-01-08 09:53:11 +0100680 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100681
682 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500683 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100684
Michal Vasko77367452021-02-16 16:32:18 +0100685 errno = 0;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000686
687 if (pthread_rwlockattr_init(&attr) == 0) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200688#if defined (HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP)
Frank Rimpler9f838b02018-07-25 06:44:03 +0000689 if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
690 if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
691 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
692 }
693 if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
694 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
695 }
696 } else {
697 ERR("%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
698 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700699#endif
Frank Rimpler9f838b02018-07-25 06:44:03 +0000700 pthread_rwlockattr_destroy(&attr);
701 } else {
702 ERR("%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
703 }
Michal Vasko086311b2016-01-08 09:53:11 +0100704 return 0;
705}
706
Michal Vaskob48aa812016-01-18 14:13:09 +0100707API void
708nc_server_destroy(void)
709{
Michal Vasko1440a742021-03-31 11:11:03 +0200710 uint32_t i;
Radek Krejci658782b2016-12-04 22:04:55 +0100711
712 for (i = 0; i < server_opts.capabilities_count; i++) {
713 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
714 }
715 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200716 server_opts.capabilities = NULL;
717 server_opts.capabilities_count = 0;
Michal Vasko1440a742021-03-31 11:11:03 +0200718 if (server_opts.content_id_data && server_opts.content_id_data_free) {
719 server_opts.content_id_data_free(server_opts.content_id_data);
720 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200721
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200722#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100723 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200724 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100725#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100726#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100727 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
728 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
729 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200730 server_opts.passwd_auth_data = NULL;
731 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100732
Michal Vasko17dfda92016-12-01 14:06:16 +0100733 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100734
735 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
736 server_opts.hostkey_data_free(server_opts.hostkey_data);
737 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200738 server_opts.hostkey_data = NULL;
739 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100740#endif
741#ifdef NC_ENABLED_TLS
742 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
743 server_opts.server_cert_data_free(server_opts.server_cert_data);
744 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200745 server_opts.server_cert_data = NULL;
746 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100747 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
748 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
749 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200750 server_opts.trusted_cert_list_data = NULL;
751 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100752#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100753 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100754}
755
Michal Vasko086311b2016-01-08 09:53:11 +0100756API int
757nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
758{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200759 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
760 ERRARG("basic_mode");
761 return -1;
762 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
763 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100764 return -1;
765 }
766
767 server_opts.wd_basic_mode = basic_mode;
768 server_opts.wd_also_supported = also_supported;
769 return 0;
770}
771
Michal Vasko1a38c862016-01-15 15:50:07 +0100772API void
Michal Vasko55f03972016-04-13 08:56:01 +0200773nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
774{
775 if (!basic_mode && !also_supported) {
776 ERRARG("basic_mode and also_supported");
777 return;
778 }
779
780 if (basic_mode) {
781 *basic_mode = server_opts.wd_basic_mode;
782 }
783 if (also_supported) {
784 *also_supported = server_opts.wd_also_supported;
785 }
786}
787
Michal Vasko55f03972016-04-13 08:56:01 +0200788API int
Radek Krejci658782b2016-12-04 22:04:55 +0100789nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200790{
Radek Krejci658782b2016-12-04 22:04:55 +0100791 const char **new;
792
793 if (!value || !value[0]) {
794 ERRARG("value must not be empty");
795 return EXIT_FAILURE;
796 }
797
798 server_opts.capabilities_count++;
799 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
800 if (!new) {
801 ERRMEM;
802 return EXIT_FAILURE;
803 }
804 server_opts.capabilities = new;
Michal Vasko77367452021-02-16 16:32:18 +0100805 lydict_insert(server_opts.ctx, value, 0, &server_opts.capabilities[server_opts.capabilities_count - 1]);
Radek Krejci658782b2016-12-04 22:04:55 +0100806
807 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200808}
809
Michal Vasko1a38c862016-01-15 15:50:07 +0100810API void
Michal Vasko1440a742021-03-31 11:11:03 +0200811nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
812 void (*free_user_data)(void *user_data))
813{
814 server_opts.content_id_clb = content_id_clb;
815 server_opts.content_id_data = user_data;
816 server_opts.content_id_data_free = free_user_data;
817}
818
819API void
Michal Vasko086311b2016-01-08 09:53:11 +0100820nc_server_set_hello_timeout(uint16_t hello_timeout)
821{
Michal Vasko086311b2016-01-08 09:53:11 +0100822 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100823}
824
Michal Vasko55f03972016-04-13 08:56:01 +0200825API uint16_t
826nc_server_get_hello_timeout(void)
827{
828 return server_opts.hello_timeout;
829}
830
Michal Vasko1a38c862016-01-15 15:50:07 +0100831API void
Michal Vasko086311b2016-01-08 09:53:11 +0100832nc_server_set_idle_timeout(uint16_t idle_timeout)
833{
Michal Vasko086311b2016-01-08 09:53:11 +0100834 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100835}
836
Michal Vasko55f03972016-04-13 08:56:01 +0200837API uint16_t
838nc_server_get_idle_timeout(void)
839{
840 return server_opts.idle_timeout;
841}
842
Michal Vasko71090fc2016-05-24 16:37:28 +0200843API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100844nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100845{
Michal Vasko71090fc2016-05-24 16:37:28 +0200846 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200847 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200848
Michal Vasko45e53ae2016-04-07 11:46:03 +0200849 if (!server_opts.ctx) {
850 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200851 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200852 } else if (fdin < 0) {
853 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200854 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200855 } else if (fdout < 0) {
856 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200857 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200858 } else if (!username) {
859 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200860 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200861 } else if (!session) {
862 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200863 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100864 }
865
866 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200867 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100868 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100869 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200870 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100871 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100872 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100873
Michal Vasko086311b2016-01-08 09:53:11 +0100874 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100875 (*session)->ti_type = NC_TI_FD;
876 (*session)->ti.fd.in = fdin;
877 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100878
879 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100880 (*session)->flags = NC_SESSION_SHAREDCTX;
881 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100882
Michal Vaskob48aa812016-01-18 14:13:09 +0100883 /* assign new SID atomically */
Michal Vasko7f1fa3c2020-09-08 16:30:41 +0200884 (*session)->id = ATOMIC_INC(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100885
Michal Vasko086311b2016-01-08 09:53:11 +0100886 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200887 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200888 if (msgtype != NC_MSG_HELLO) {
889 nc_session_free(*session, NULL);
890 *session = NULL;
891 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100892 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200893
894 nc_gettimespec_mono(&ts_cur);
895 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
896 nc_gettimespec_real(&ts_cur);
897 (*session)->opts.server.session_start = ts_cur.tv_sec;
898
Michal Vasko1a38c862016-01-15 15:50:07 +0100899 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100900
Michal Vasko71090fc2016-05-24 16:37:28 +0200901 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100902}
Michal Vasko9e036d52016-01-08 10:49:26 +0100903
Michal Vaskob30b99c2016-07-26 11:35:43 +0200904static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100905nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
906{
907 uint8_t q_last;
908
909 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
910 ERRINT;
911 return;
912 }
913
914 /* get a unique queue value (by adding 1 to the last added value, if any) */
915 if (ps->queue_len) {
916 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
917 *id = ps->queue[q_last] + 1;
918 } else {
919 *id = 0;
920 }
921
922 /* add the id into the queue */
923 ++ps->queue_len;
924 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
925 ps->queue[q_last] = *id;
926}
927
928static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200929nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
930{
Michal Vasko74c345f2018-02-07 10:37:11 +0100931 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200932
933 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100934 /* get the actual queue idx */
935 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200936
937 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100938 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200939 /* another equal value, simply cannot be */
940 ERRINT;
941 }
Michal Vaskod8340032018-02-12 14:41:00 +0100942 if (found == 2) {
943 /* move the following values */
944 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
945 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100946 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200947 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100948 if (i == 0) {
949 found = 1;
950 } else {
951 /* this is not okay, our id is in the middle of the queue */
952 found = 2;
953 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200954 }
955 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200956 if (!found) {
957 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100958 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200959 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100960
Michal Vasko103fe632018-02-12 16:37:45 +0100961 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100962 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100963 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100964 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
965 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200966}
967
Michal Vaskof04a52a2016-04-07 10:52:10 +0200968int
Michal Vasko26043172016-07-26 14:08:59 +0200969nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200970{
971 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200972 struct timespec ts;
973
Michal Vasko77a6abe2017-10-05 10:02:20 +0200974 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100975 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200976
977 /* LOCK */
978 ret = pthread_mutex_timedlock(&ps->lock, &ts);
979 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200980 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200981 return -1;
982 }
983
Michal Vasko74c345f2018-02-07 10:37:11 +0100984 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +0100985 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100986 ERR("%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +0100987 pthread_mutex_unlock(&ps->lock);
988 return -1;
989 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100990
991 /* add ourselves into the queue */
992 nc_ps_queue_add_id(ps, id);
Michal Vasko2733aad2020-04-16 09:01:52 +0200993 DBL("PS 0x%p TID %lu queue: added %u, head %u, lenght %u", ps, (long unsigned int)pthread_self(), *id,
Michal Vasko91290952019-09-27 11:30:55 +0200994 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200995
996 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200997 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200998 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +0100999 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001000
1001 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
1002 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +05301003 /**
1004 * This may happen when another thread releases the lock and broadcasts the condition
1005 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
1006 * but when actually this thread was ready for condition.
1007 */
preetbhansali629dfc42018-12-17 16:04:40 +05301008 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +05301009 break;
1010 }
Michal Vasko66032bc2019-01-22 15:03:12 +01001011
Michal Vasko26043172016-07-26 14:08:59 +02001012 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001013 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001014 nc_ps_queue_remove_id(ps, *id);
1015 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001016 return -1;
1017 }
1018 }
1019
Michal Vaskobe86fe32016-04-07 10:43:03 +02001020 /* UNLOCK */
1021 pthread_mutex_unlock(&ps->lock);
1022
1023 return 0;
1024}
1025
Michal Vaskof04a52a2016-04-07 10:52:10 +02001026int
Michal Vasko26043172016-07-26 14:08:59 +02001027nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001028{
1029 int ret;
1030 struct timespec ts;
1031
Michal Vasko77a6abe2017-10-05 10:02:20 +02001032 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +01001033 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001034
1035 /* LOCK */
1036 ret = pthread_mutex_timedlock(&ps->lock, &ts);
1037 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +02001038 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001039 ret = -1;
1040 }
1041
Michal Vaskob30b99c2016-07-26 11:35:43 +02001042 /* we must be the first, it was our turn after all, right? */
1043 if (ps->queue[ps->queue_begin] != id) {
1044 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +02001045 /* UNLOCK */
1046 if (!ret) {
1047 pthread_mutex_unlock(&ps->lock);
1048 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001049 return -1;
1050 }
1051
Michal Vaskobe86fe32016-04-07 10:43:03 +02001052 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001053 nc_ps_queue_remove_id(ps, id);
Michal Vasko2733aad2020-04-16 09:01:52 +02001054 DBL("PS 0x%p TID %lu queue: removed %u, head %u, lenght %u", ps, (long unsigned int)pthread_self(), id,
Michal Vasko91290952019-09-27 11:30:55 +02001055 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001056
1057 /* broadcast to all other threads that the queue moved */
1058 pthread_cond_broadcast(&ps->cond);
1059
Michal Vaskobe86fe32016-04-07 10:43:03 +02001060 /* UNLOCK */
1061 if (!ret) {
1062 pthread_mutex_unlock(&ps->lock);
1063 }
1064
1065 return ret;
1066}
1067
Michal Vasko428087d2016-01-14 16:04:28 +01001068API struct nc_pollsession *
1069nc_ps_new(void)
1070{
Michal Vasko48a63ed2016-03-01 09:48:21 +01001071 struct nc_pollsession *ps;
1072
1073 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +01001074 if (!ps) {
1075 ERRMEM;
1076 return NULL;
1077 }
Michal Vaskobe86fe32016-04-07 10:43:03 +02001078 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001079 pthread_mutex_init(&ps->lock, NULL);
1080
1081 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +01001082}
1083
1084API void
1085nc_ps_free(struct nc_pollsession *ps)
1086{
fanchanghu3d4e7212017-08-09 09:42:30 +08001087 uint16_t i;
1088
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001089 if (!ps) {
1090 return;
1091 }
1092
Michal Vaskobe86fe32016-04-07 10:43:03 +02001093 if (ps->queue_len) {
1094 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
1095 }
1096
fanchanghu3d4e7212017-08-09 09:42:30 +08001097 for (i = 0; i < ps->session_count; i++) {
1098 free(ps->sessions[i]);
1099 }
1100
Michal Vasko428087d2016-01-14 16:04:28 +01001101 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001102 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001103 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001104
Michal Vasko428087d2016-01-14 16:04:28 +01001105 free(ps);
1106}
1107
1108API int
1109nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1110{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001111 uint8_t q_id;
1112
Michal Vasko45e53ae2016-04-07 11:46:03 +02001113 if (!ps) {
1114 ERRARG("ps");
1115 return -1;
1116 } else if (!session) {
1117 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001118 return -1;
1119 }
1120
Michal Vasko48a63ed2016-03-01 09:48:21 +01001121 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001122 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001123 return -1;
1124 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001125
Michal Vasko428087d2016-01-14 16:04:28 +01001126 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001127 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001128 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001129 ERRMEM;
1130 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001131 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001132 return -1;
1133 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001134 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1135 if (!ps->sessions[ps->session_count - 1]) {
1136 ERRMEM;
1137 --ps->session_count;
1138 /* UNLOCK */
1139 nc_ps_unlock(ps, q_id, __func__);
1140 return -1;
1141 }
1142 ps->sessions[ps->session_count - 1]->session = session;
1143 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001144
Michal Vasko48a63ed2016-03-01 09:48:21 +01001145 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001146 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001147}
1148
Michal Vasko48a63ed2016-03-01 09:48:21 +01001149static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001150_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001151{
1152 uint16_t i;
1153
Radek Krejcid5f978f2016-03-03 13:14:45 +01001154 if (index >= 0) {
1155 i = (uint16_t)index;
1156 goto remove;
1157 }
Michal Vasko428087d2016-01-14 16:04:28 +01001158 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001159 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001160remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001161 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001162 if (i <= ps->session_count) {
1163 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001164 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001165 }
1166 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001167 free(ps->sessions);
1168 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001169 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001170 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001171 return 0;
1172 }
1173 }
1174
Michal Vaskof0537d82016-01-29 14:42:38 +01001175 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001176}
1177
Michal Vasko48a63ed2016-03-01 09:48:21 +01001178API int
1179nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1180{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001181 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001182 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001183
Michal Vasko45e53ae2016-04-07 11:46:03 +02001184 if (!ps) {
1185 ERRARG("ps");
1186 return -1;
1187 } else if (!session) {
1188 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001189 return -1;
1190 }
1191
1192 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001193 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001194 return -1;
1195 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001196
Radek Krejcid5f978f2016-03-03 13:14:45 +01001197 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001198
1199 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001200 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001201
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001202 return ret || ret2 ? -1 : 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001203}
1204
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001205API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001206nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001207{
1208 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001209 struct nc_session *ret = NULL;
1210
1211 if (!ps) {
1212 ERRARG("ps");
1213 return NULL;
1214 }
1215
1216 /* LOCK */
1217 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1218 return NULL;
1219 }
1220
Michal Vasko4871c9d2017-10-09 14:48:39 +02001221 if (idx < ps->session_count) {
1222 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001223 }
1224
1225 /* UNLOCK */
1226 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1227
1228 return ret;
1229}
1230
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001231API uint16_t
1232nc_ps_session_count(struct nc_pollsession *ps)
1233{
Michal Vasko47003942019-03-14 12:25:23 +01001234 uint8_t q_id;
1235 uint16_t session_count;
1236
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001237 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001238 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001239 return 0;
1240 }
1241
Michal Vasko47003942019-03-14 12:25:23 +01001242 /* LOCK (just for memory barrier so that we read the current value) */
1243 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1244 return 0;
1245 }
1246
1247 session_count = ps->session_count;
1248
1249 /* UNLOCK */
1250 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1251
1252 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001253}
1254
Michal Vasko131120a2018-05-29 15:44:02 +02001255/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001256 * returns: NC_PSPOLL_ERROR,
Michal Vasko77367452021-02-16 16:32:18 +01001257 * NC_PSPOLL_TIMEOUT,
Michal Vasko71090fc2016-05-24 16:37:28 +02001258 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1259 * NC_PSPOLL_RPC
1260 */
1261static int
Michal Vasko131120a2018-05-29 15:44:02 +02001262nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001263{
Michal Vasko77367452021-02-16 16:32:18 +01001264 struct ly_in *msg;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001265 struct nc_server_reply *reply = NULL;
Michal Vasko939ffce2021-04-12 13:02:01 +02001266 struct lyd_node *e;
Michal Vasko77367452021-02-16 16:32:18 +01001267 int r, ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001268
Michal Vasko45e53ae2016-04-07 11:46:03 +02001269 if (!session) {
1270 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001271 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001272 } else if (!rpc) {
1273 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001274 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001275 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001276 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001277 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001278 }
1279
Michal Vasko77367452021-02-16 16:32:18 +01001280 /* get a message */
1281 r = nc_read_msg_io(session, io_timeout, &msg, 0);
1282 if (r == -2) {
1283 /* malformed message */
1284 ret = NC_PSPOLL_REPLY_ERROR;
1285 reply = nc_server_reply_err(nc_err(server_opts.ctx, NC_ERR_MALFORMED_MSG));
1286 goto send_reply;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001287 }
1288 if (r == -1) {
Michal Vasko77367452021-02-16 16:32:18 +01001289 return NC_PSPOLL_ERROR;
1290 } else if (!r) {
1291 return NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001292 }
1293
Michal Vasko77367452021-02-16 16:32:18 +01001294 *rpc = calloc(1, sizeof **rpc);
1295 if (!*rpc) {
1296 ERRMEM;
1297 ret = NC_PSPOLL_REPLY_ERROR;
1298 goto cleanup;
1299 }
1300
1301 /* parse the RPC */
1302 if (lyd_parse_op(server_opts.ctx, NULL, msg, LYD_XML, LYD_TYPE_RPC_NETCONF, &(*rpc)->envp, &(*rpc)->rpc)) {
1303 /* bad RPC received */
1304 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1305
1306 if ((*rpc)->envp) {
1307 /* at least the envelopes were parsed */
Michal Vasko939ffce2021-04-12 13:02:01 +02001308 e = nc_err(server_opts.ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
1309 nc_err_set_msg(e, ly_errmsg(server_opts.ctx), "en");
1310 reply = nc_server_reply_err(e);
Michal Vasko77367452021-02-16 16:32:18 +01001311 } else if (session->version == NC_VERSION_11) {
1312 /* completely malformed message, NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
1313 reply = nc_server_reply_err(nc_err(server_opts.ctx, NC_ERR_MALFORMED_MSG));
1314 }
1315
1316send_reply:
1317 if (reply) {
1318 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, (*rpc)->envp, reply);
1319 nc_server_reply_free(reply);
1320 if (r != NC_MSG_REPLY) {
1321 ERR("Session %u: failed to write reply (%s), terminating session.", session->id, nc_msgtype2str[r]);
1322 if (session->status != NC_STATUS_INVALID) {
1323 session->status = NC_STATUS_INVALID;
1324 session->term_reason = NC_SESSION_TERM_OTHER;
1325 }
1326 }
1327 }
1328 } else {
1329 ret = NC_PSPOLL_RPC;
1330 }
1331
1332cleanup:
1333 ly_in_free(msg, 1);
1334 if (ret != NC_PSPOLL_RPC) {
1335 nc_server_rpc_free(*rpc);
1336 *rpc = NULL;
1337 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001338 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001339}
1340
fanchanghu966f2de2016-07-21 02:28:57 -04001341API void
1342nc_set_global_rpc_clb(nc_rpc_clb clb)
1343{
1344 global_rpc_clb = clb;
1345}
1346
Radek Krejci93e80222016-10-03 13:34:25 +02001347API NC_MSG_TYPE
1348nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1349{
Michal Vasko131120a2018-05-29 15:44:02 +02001350 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001351
1352 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001353 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001354 ERRARG("session");
1355 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001356 } else if (!notif || !notif->ntf || !notif->eventtime) {
Radek Krejci93e80222016-10-03 13:34:25 +02001357 ERRARG("notif");
1358 return NC_MSG_ERROR;
1359 }
1360
Michal Vasko131120a2018-05-29 15:44:02 +02001361 /* we do not need RPC lock for this, IO lock will be acquired properly */
1362 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001363 if (ret != NC_MSG_NOTIF) {
1364 ERR("Session %u: failed to write notification (%s).", session->id, nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001365 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001366
Michal Vasko131120a2018-05-29 15:44:02 +02001367 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001368}
1369
Michal Vasko131120a2018-05-29 15:44:02 +02001370/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001371 * returns: NC_PSPOLL_ERROR,
1372 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1373 * NC_PSPOLL_REPLY_ERROR,
1374 * 0
1375 */
1376static int
Michal Vasko131120a2018-05-29 15:44:02 +02001377nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001378{
1379 nc_rpc_clb clb;
1380 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +01001381 const struct lysc_node *rpc_act = NULL;
1382 struct lyd_node *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001383 int ret = 0;
1384 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001385
Michal Vasko4a827e52016-03-03 10:59:00 +01001386 if (!rpc) {
1387 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001388 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001389 }
1390
Michal Vasko77367452021-02-16 16:32:18 +01001391 if (rpc->rpc->schema->nodetype == LYS_RPC) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001392 /* RPC */
Michal Vasko77367452021-02-16 16:32:18 +01001393 rpc_act = rpc->rpc->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001394 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001395 /* action */
Michal Vasko77367452021-02-16 16:32:18 +01001396 LYD_TREE_DFS_BEGIN(rpc->rpc, elem) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001397 if (elem->schema->nodetype == LYS_ACTION) {
1398 rpc_act = elem->schema;
1399 break;
1400 }
Michal Vasko77367452021-02-16 16:32:18 +01001401 LYD_TREE_DFS_END(rpc->rpc, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001402 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001403 if (!rpc_act) {
1404 ERRINT;
1405 return NC_PSPOLL_ERROR;
1406 }
1407 }
1408
1409 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001410 if (!global_rpc_clb) {
1411 /* no callback, reply with a not-implemented error */
Michal Vasko77367452021-02-16 16:32:18 +01001412 reply = nc_server_reply_err(nc_err(server_opts.ctx, NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001413 } else {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001414 reply = global_rpc_clb(rpc->rpc, session);
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001415 }
Michal Vasko428087d2016-01-14 16:04:28 +01001416 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001417 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko77367452021-02-16 16:32:18 +01001418 reply = clb(rpc->rpc, session);
Michal Vasko428087d2016-01-14 16:04:28 +01001419 }
1420
1421 if (!reply) {
Michal Vasko77367452021-02-16 16:32:18 +01001422 reply = nc_server_reply_err(nc_err(server_opts.ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001423 }
Michal Vasko77367452021-02-16 16:32:18 +01001424 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->envp, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001425 if (reply->type == NC_RPL_ERROR) {
1426 ret |= NC_PSPOLL_REPLY_ERROR;
1427 }
1428 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001429
Michal Vasko131120a2018-05-29 15:44:02 +02001430 if (r != NC_MSG_REPLY) {
Michal Vasko8fe604c2020-02-10 15:25:04 +01001431 ERR("Session %u: failed to write reply (%s).", session->id, nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001432 ret |= NC_PSPOLL_ERROR;
1433 }
Michal Vasko428087d2016-01-14 16:04:28 +01001434
1435 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1436 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1437 session->status = NC_STATUS_INVALID;
1438 }
1439
Michal Vasko71090fc2016-05-24 16:37:28 +02001440 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001441}
1442
Michal Vasko131120a2018-05-29 15:44:02 +02001443/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001444 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1445 * NC_PSPOLL_ERROR, (msg filled)
1446 * NC_PSPOLL_TIMEOUT,
1447 * NC_PSPOLL_RPC (some application data available),
1448 * NC_PSPOLL_SSH_CHANNEL,
1449 * NC_PSPOLL_SSH_MSG
1450 */
1451static int
Michal Vasko131120a2018-05-29 15:44:02 +02001452nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001453{
Michal Vasko9a327362017-01-11 11:31:46 +01001454 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001455 int r, ret = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001456
Michal Vasko9a327362017-01-11 11:31:46 +01001457#ifdef NC_ENABLED_SSH
1458 struct nc_session *new;
1459#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001460
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001461 /* check timeout first */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001462 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout &&
1463 (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001464 sprintf(msg, "session idle timeout elapsed");
1465 session->status = NC_STATUS_INVALID;
1466 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1467 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1468 }
1469
Michal Vasko131120a2018-05-29 15:44:02 +02001470 r = nc_session_io_lock(session, io_timeout, __func__);
1471 if (r < 0) {
1472 sprintf(msg, "session IO lock failed to be acquired");
1473 return NC_PSPOLL_ERROR;
1474 } else if (!r) {
1475 return NC_PSPOLL_TIMEOUT;
1476 }
1477
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001478 switch (session->ti_type) {
1479#ifdef NC_ENABLED_SSH
1480 case NC_TI_LIBSSH:
1481 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001482 if (r == SSH_EOF) {
1483 sprintf(msg, "SSH channel unexpected EOF");
1484 session->status = NC_STATUS_INVALID;
1485 session->term_reason = NC_SESSION_TERM_DROPPED;
1486 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1487 } else if (r == SSH_ERROR) {
1488 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001489 session->status = NC_STATUS_INVALID;
1490 session->term_reason = NC_SESSION_TERM_OTHER;
1491 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001492 } else if (!r) {
1493 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1494 /* new SSH message */
1495 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1496 if (session->ti.libssh.next) {
1497 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001498 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel &&
1499 (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko8dcaa882017-10-19 14:28:42 +02001500 /* new NETCONF SSH channel */
1501 ret = NC_PSPOLL_SSH_CHANNEL;
1502 break;
1503 }
1504 }
1505 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001506 break;
1507 }
1508 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001509
Michal Vasko8dcaa882017-10-19 14:28:42 +02001510 /* just some SSH message */
1511 ret = NC_PSPOLL_SSH_MSG;
1512 } else {
1513 ret = NC_PSPOLL_TIMEOUT;
1514 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001515 } else {
1516 /* we have some application data */
1517 ret = NC_PSPOLL_RPC;
1518 }
1519 break;
1520#endif
1521#ifdef NC_ENABLED_TLS
1522 case NC_TI_OPENSSL:
1523 r = SSL_pending(session->ti.tls);
1524 if (!r) {
1525 /* no data pending in the SSL buffer, poll fd */
1526 pfd.fd = SSL_get_rfd(session->ti.tls);
1527 if (pfd.fd < 0) {
1528 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1529 ret = NC_PSPOLL_ERROR;
1530 break;
1531 }
1532 pfd.events = POLLIN;
1533 pfd.revents = 0;
1534 r = poll(&pfd, 1, 0);
1535
1536 if ((r < 0) && (errno != EINTR)) {
1537 sprintf(msg, "poll failed (%s)", strerror(errno));
1538 session->status = NC_STATUS_INVALID;
1539 ret = NC_PSPOLL_ERROR;
1540 } else if (r > 0) {
1541 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1542 sprintf(msg, "communication socket unexpectedly closed");
1543 session->status = NC_STATUS_INVALID;
1544 session->term_reason = NC_SESSION_TERM_DROPPED;
1545 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1546 } else if (pfd.revents & POLLERR) {
1547 sprintf(msg, "communication socket error");
1548 session->status = NC_STATUS_INVALID;
1549 session->term_reason = NC_SESSION_TERM_OTHER;
1550 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1551 } else {
1552 ret = NC_PSPOLL_RPC;
1553 }
1554 } else {
1555 ret = NC_PSPOLL_TIMEOUT;
1556 }
1557 } else {
1558 ret = NC_PSPOLL_RPC;
1559 }
1560 break;
1561#endif
1562 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001563 case NC_TI_UNIX:
1564 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001565 pfd.events = POLLIN;
1566 pfd.revents = 0;
1567 r = poll(&pfd, 1, 0);
1568
1569 if ((r < 0) && (errno != EINTR)) {
1570 sprintf(msg, "poll failed (%s)", strerror(errno));
1571 session->status = NC_STATUS_INVALID;
1572 ret = NC_PSPOLL_ERROR;
1573 } else if (r > 0) {
1574 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1575 sprintf(msg, "communication socket unexpectedly closed");
1576 session->status = NC_STATUS_INVALID;
1577 session->term_reason = NC_SESSION_TERM_DROPPED;
1578 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1579 } else if (pfd.revents & POLLERR) {
1580 sprintf(msg, "communication socket error");
1581 session->status = NC_STATUS_INVALID;
1582 session->term_reason = NC_SESSION_TERM_OTHER;
1583 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1584 } else {
1585 ret = NC_PSPOLL_RPC;
1586 }
1587 } else {
1588 ret = NC_PSPOLL_TIMEOUT;
1589 }
1590 break;
1591 case NC_TI_NONE:
1592 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1593 ret = NC_PSPOLL_ERROR;
1594 break;
1595 }
1596
Michal Vasko131120a2018-05-29 15:44:02 +02001597 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001598 return ret;
1599}
1600
1601API int
1602nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1603{
1604 int ret, r;
1605 uint8_t q_id;
1606 uint16_t i, j;
1607 char msg[256];
1608 struct timespec ts_timeout, ts_cur;
1609 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001610 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001611 struct nc_server_rpc *rpc = NULL;
1612
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001613 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001614 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001615 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001616 }
1617
Michal Vaskoade892d2017-02-22 13:40:35 +01001618 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001619 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001620 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001621 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001622
Michal Vaskoade892d2017-02-22 13:40:35 +01001623 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001624 nc_ps_unlock(ps, q_id, __func__);
1625 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001626 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001627
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001628 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001629 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001630 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001631 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001632 nc_addtimespec(&ts_timeout, timeout);
1633 }
1634
1635 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001636 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001637 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001638 if (ps->last_event_session == ps->session_count - 1) {
1639 i = j = 0;
1640 } else {
1641 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001642 }
Michal Vasko9a327362017-01-11 11:31:46 +01001643 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001644 cur_ps_session = ps->sessions[i];
1645 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001646
Michal Vasko131120a2018-05-29 15:44:02 +02001647 /* SESSION RPC LOCK */
1648 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001649 if (r == -1) {
1650 ret = NC_PSPOLL_ERROR;
1651 } else if (r == 1) {
1652 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001653 switch (cur_ps_session->state) {
1654 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001655 if (cur_session->status == NC_STATUS_RUNNING) {
1656 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001657 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001658
Michal Vasko131120a2018-05-29 15:44:02 +02001659 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001660 switch (ret) {
1661 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1662 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001663 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001664 break;
1665 case NC_PSPOLL_ERROR:
1666 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001667 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001668 break;
1669 case NC_PSPOLL_TIMEOUT:
1670#ifdef NC_ENABLED_SSH
1671 case NC_PSPOLL_SSH_CHANNEL:
1672 case NC_PSPOLL_SSH_MSG:
1673#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001674 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001675 break;
1676 case NC_PSPOLL_RPC:
1677 /* let's keep the state busy, we are not done with this session */
1678 break;
1679 }
1680 } else {
1681 /* session is not fine, let the caller know */
1682 ret = NC_PSPOLL_SESSION_TERM;
1683 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1684 ret |= NC_PSPOLL_SESSION_ERROR;
1685 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001686 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001687 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001688 break;
1689 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001690 /* it definitely should not be busy because we have the lock */
1691 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001692 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001693 break;
1694 case NC_PS_STATE_INVALID:
1695 /* we got it locked, but it will be freed, let it be */
1696 ret = NC_PSPOLL_TIMEOUT;
1697 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001698 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001699
Michal Vasko131120a2018-05-29 15:44:02 +02001700 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001701 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001702 /* SESSION RPC UNLOCK */
1703 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001704 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001705 } else {
1706 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001707 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001708 }
Michal Vasko428087d2016-01-14 16:04:28 +01001709
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001710 /* something happened */
1711 if (ret != NC_PSPOLL_TIMEOUT) {
1712 break;
1713 }
1714
Michal Vasko9a327362017-01-11 11:31:46 +01001715 if (i == ps->session_count - 1) {
1716 i = 0;
1717 } else {
1718 ++i;
1719 }
1720 } while (i != j);
1721
Michal Vaskoade892d2017-02-22 13:40:35 +01001722 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001723 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001724 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001725 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001726 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001727
1728 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1729 /* final timeout */
1730 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001731 }
Michal Vasko428087d2016-01-14 16:04:28 +01001732 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001733 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001734
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001735 /* do we want to return the session? */
1736 switch (ret) {
1737 case NC_PSPOLL_RPC:
1738 case NC_PSPOLL_SESSION_TERM:
1739 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1740#ifdef NC_ENABLED_SSH
1741 case NC_PSPOLL_SSH_CHANNEL:
1742 case NC_PSPOLL_SSH_MSG:
1743#endif
1744 if (session) {
1745 *session = cur_session;
1746 }
1747 ps->last_event_session = i;
1748 break;
1749 default:
1750 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001751 }
Michal Vasko428087d2016-01-14 16:04:28 +01001752
Michal Vaskoade892d2017-02-22 13:40:35 +01001753 /* PS UNLOCK */
1754 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001755
Michal Vasko131120a2018-05-29 15:44:02 +02001756 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001757 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001758 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001759 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1760 if (cur_session->status != NC_STATUS_RUNNING) {
1761 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001762 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001763 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001764 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001765 }
1766 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001767 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001768
Michal Vasko7f1ee932018-10-11 09:41:42 +02001769 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001770 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001771 if (cur_session->status != NC_STATUS_RUNNING) {
1772 ret |= NC_PSPOLL_SESSION_TERM;
1773 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1774 ret |= NC_PSPOLL_SESSION_ERROR;
1775 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001776 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001777 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001778 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001779 }
Michal Vasko428087d2016-01-14 16:04:28 +01001780 }
Michal Vasko77367452021-02-16 16:32:18 +01001781 nc_server_rpc_free(rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001782
Michal Vasko131120a2018-05-29 15:44:02 +02001783 /* SESSION RPC UNLOCK */
1784 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001785 }
1786
Michal Vasko48a63ed2016-03-01 09:48:21 +01001787 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001788}
1789
Michal Vaskod09eae62016-02-01 10:32:52 +01001790API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001791nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001792{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001793 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001794 uint16_t i;
1795 struct nc_session *session;
1796
Michal Vasko9a25e932016-02-01 10:36:42 +01001797 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001798 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001799 return;
1800 }
1801
Michal Vasko48a63ed2016-03-01 09:48:21 +01001802 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001803 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001804 return;
1805 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001806
Michal Vasko48a63ed2016-03-01 09:48:21 +01001807 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001808 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001809 nc_session_free(ps->sessions[i]->session, data_free);
1810 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001811 }
1812 free(ps->sessions);
1813 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001814 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001815 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001816 } else {
1817 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001818 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1819 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001820 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001821 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001822 continue;
1823 }
1824
1825 ++i;
1826 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001827 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001828
1829 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001830 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001831}
1832
Michal Vasko5f352c52019-07-10 16:12:06 +02001833static int
apropp-molex4e903c32020-04-20 03:06:58 -04001834nc_get_uid(int sock, uid_t *uid)
1835{
Michal Vaskod3910912020-04-20 09:12:49 +02001836 int ret;
apropp-molex4e903c32020-04-20 03:06:58 -04001837
Michal Vaskod3910912020-04-20 09:12:49 +02001838#ifdef SO_PEERCRED
1839 struct ucred ucred;
1840 socklen_t len;
1841 len = sizeof(ucred);
1842 ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
1843 if (!ret) {
1844 *uid = ucred.uid;
1845 }
1846#else
1847 ret = getpeereid(sock, uid, NULL);
1848#endif
1849
1850 if (ret < 0) {
1851 ERR("Failed to get credentials from unix socket (%s).", strerror(errno));
1852 return -1;
1853 }
apropp-molex4e903c32020-04-20 03:06:58 -04001854 return 0;
1855}
1856
1857static int
Michal Vasko5f352c52019-07-10 16:12:06 +02001858nc_accept_unix(struct nc_session *session, int sock)
1859{
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001860#if defined (SO_PEERCRED) || defined (HAVE_GETPEEREID)
Michal Vasko5f352c52019-07-10 16:12:06 +02001861 const struct passwd *pw;
Michal Vasko5f352c52019-07-10 16:12:06 +02001862 char *username;
Michal Vasko5f352c52019-07-10 16:12:06 +02001863 session->ti_type = NC_TI_UNIX;
apropp-molex4e903c32020-04-20 03:06:58 -04001864 uid_t uid;
Michal Vasko5f352c52019-07-10 16:12:06 +02001865
Michal Vaskod3910912020-04-20 09:12:49 +02001866 if (nc_get_uid(sock, &uid)) {
1867 close(sock);
Michal Vasko5f352c52019-07-10 16:12:06 +02001868 return -1;
1869 }
1870
apropp-molex4e903c32020-04-20 03:06:58 -04001871 pw = getpwuid(uid);
Michal Vasko5f352c52019-07-10 16:12:06 +02001872 if (pw == NULL) {
Michal Vasko77367452021-02-16 16:32:18 +01001873 ERR("Failed to find username for uid=%u (%s).\n", uid, strerror(errno));
Michal Vasko5f352c52019-07-10 16:12:06 +02001874 close(sock);
1875 return -1;
1876 }
1877
1878 username = strdup(pw->pw_name);
1879 if (username == NULL) {
1880 ERRMEM;
1881 close(sock);
1882 return -1;
1883 }
Michal Vasko77367452021-02-16 16:32:18 +01001884 lydict_insert_zc(server_opts.ctx, username, &session->username);
Michal Vasko5f352c52019-07-10 16:12:06 +02001885
1886 session->ti.unixsock.sock = sock;
1887
1888 return 1;
Claus Klein22091912020-01-20 13:45:47 +01001889#else
1890 return -1;
1891#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02001892}
1893
Michal Vaskoe2713da2016-08-22 16:06:40 +02001894API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001895nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001896{
Michal Vasko3031aae2016-01-27 16:07:18 +01001897 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001898 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001899
Michal Vasko45e53ae2016-04-07 11:46:03 +02001900 if (!name) {
1901 ERRARG("name");
1902 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001903 }
1904
Michal Vaskoade892d2017-02-22 13:40:35 +01001905 /* BIND LOCK */
1906 pthread_mutex_lock(&server_opts.bind_lock);
1907
1908 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001909 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001910
1911 /* check name uniqueness */
1912 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001913 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001914 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001915 ret = -1;
1916 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001917 }
1918 }
1919
Michal Vasko3031aae2016-01-27 16:07:18 +01001920 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001921 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001922 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001923 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001924 ret = -1;
1925 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001926 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001927 memset(&server_opts.endpts[server_opts.endpt_count - 1], 0, sizeof *server_opts.endpts);
Michal Vasko77367452021-02-16 16:32:18 +01001928 lydict_insert(server_opts.ctx, name, 0, &server_opts.endpts[server_opts.endpt_count - 1].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001929 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001930 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
1931 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
1932 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001933
Michal Vaskoe2713da2016-08-22 16:06:40 +02001934 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001935 if (!server_opts.binds) {
1936 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001937 ret = -1;
1938 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001939 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001940
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001941 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001942 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1943
1944 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001945#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001946 case NC_TI_LIBSSH:
1947 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1948 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1949 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001950 ret = -1;
1951 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001952 }
1953 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
Michal Vasko77367452021-02-16 16:32:18 +01001954 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001955 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02001956 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001957 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001958#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001959#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001960 case NC_TI_OPENSSL:
1961 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1962 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1963 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001964 ret = -1;
1965 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001966 }
1967 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001968#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001969 case NC_TI_UNIX:
1970 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1971 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1972 ERRMEM;
1973 ret = -1;
1974 goto cleanup;
1975 }
1976 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1977 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1978 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1979 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001980 default:
1981 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001982 ret = -1;
1983 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001984 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001985
Michal Vaskoade892d2017-02-22 13:40:35 +01001986cleanup:
1987 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001988 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001989
Michal Vaskoade892d2017-02-22 13:40:35 +01001990 /* BIND UNLOCK */
1991 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001992
Michal Vaskoade892d2017-02-22 13:40:35 +01001993 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001994}
1995
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001996API int
Michal Vasko59050372016-11-22 14:33:55 +01001997nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001998{
1999 uint32_t i;
2000 int ret = -1;
2001
Michal Vaskoade892d2017-02-22 13:40:35 +01002002 /* BIND LOCK */
2003 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002004
Michal Vaskoade892d2017-02-22 13:40:35 +01002005 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002006 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002007
Michal Vasko59050372016-11-22 14:33:55 +01002008 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002009 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01002010 for (i = 0; i < server_opts.endpt_count; ++i) {
2011 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002012 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002013#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002014 case NC_TI_LIBSSH:
2015 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2016 free(server_opts.endpts[i].opts.ssh);
2017 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002018#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002019#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002020 case NC_TI_OPENSSL:
2021 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2022 free(server_opts.endpts[i].opts.tls);
2023 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002024#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002025 case NC_TI_UNIX:
2026 free(server_opts.endpts[i].opts.unixsock);
2027 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002028 default:
2029 ERRINT;
2030 /* won't get here ...*/
2031 break;
2032 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002033 ret = 0;
2034 }
Michal Vasko3031aae2016-01-27 16:07:18 +01002035 free(server_opts.endpts);
2036 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002037
2038 /* remove all binds */
2039 for (i = 0; i < server_opts.endpt_count; ++i) {
2040 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2041 if (server_opts.binds[i].sock > -1) {
2042 close(server_opts.binds[i].sock);
2043 }
2044 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002045 free(server_opts.binds);
2046 server_opts.binds = NULL;
2047
Michal Vasko3031aae2016-01-27 16:07:18 +01002048 server_opts.endpt_count = 0;
2049
Michal Vasko1a38c862016-01-15 15:50:07 +01002050 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002051 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002052 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002053 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002054 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01002055 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002056 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002057#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002058 case NC_TI_LIBSSH:
2059 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2060 free(server_opts.endpts[i].opts.ssh);
2061 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002062#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002063#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002064 case NC_TI_OPENSSL:
2065 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2066 free(server_opts.endpts[i].opts.tls);
2067 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002068#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002069 case NC_TI_UNIX:
2070 free(server_opts.endpts[i].opts.unixsock);
2071 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002072 default:
2073 ERRINT;
2074 break;
2075 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002076
Michal Vaskoe2713da2016-08-22 16:06:40 +02002077 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02002078 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2079 if (server_opts.binds[i].sock > -1) {
2080 close(server_opts.binds[i].sock);
2081 }
2082
2083 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002084 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002085 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002086 free(server_opts.binds);
2087 server_opts.binds = NULL;
2088 free(server_opts.endpts);
2089 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002090 } else if (i < server_opts.endpt_count) {
2091 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2092 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002093 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002094
2095 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002096 if (name) {
2097 break;
2098 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002099 }
2100 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002101 }
2102
Michal Vaskoade892d2017-02-22 13:40:35 +01002103 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002104 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002105
Michal Vaskoade892d2017-02-22 13:40:35 +01002106 /* BIND UNLOCK */
2107 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002108
2109 return ret;
2110}
2111
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002112API int
2113nc_server_endpt_count(void)
2114{
2115 return server_opts.endpt_count;
2116}
2117
Michal Vasko1b5973e2020-01-30 16:05:46 +01002118API int
2119nc_server_is_endpt(const char *name)
2120{
2121 uint16_t i;
2122 int found = 0;
2123
Michal Vaskofb1724b2020-01-31 11:02:00 +01002124 if (!name) {
2125 return found;
2126 }
2127
Michal Vasko1b5973e2020-01-30 16:05:46 +01002128 /* ENDPT READ LOCK */
2129 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2130
2131 /* check name uniqueness */
2132 for (i = 0; i < server_opts.endpt_count; ++i) {
2133 if (!strcmp(server_opts.endpts[i].name, name)) {
2134 found = 1;
2135 break;
2136 }
2137 }
2138
2139 /* ENDPT UNLOCK */
2140 pthread_rwlock_unlock(&server_opts.endpt_lock);
2141
2142 return found;
2143}
2144
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002145int
2146nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
2147{
2148 struct nc_endpt *endpt;
2149 struct nc_bind *bind = NULL;
2150 uint16_t i;
2151 int sock = -1, set_addr, ret = 0;
2152
2153 if (!endpt_name) {
2154 ERRARG("endpt_name");
2155 return -1;
2156 } else if ((!address && !port) || (address && port)) {
2157 ERRARG("address and port");
2158 return -1;
2159 }
2160
2161 if (address) {
2162 set_addr = 1;
2163 } else {
2164 set_addr = 0;
2165 }
2166
2167 /* BIND LOCK */
2168 pthread_mutex_lock(&server_opts.bind_lock);
2169
2170 /* ENDPT LOCK */
2171 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2172 if (!endpt) {
2173 /* BIND UNLOCK */
2174 pthread_mutex_unlock(&server_opts.bind_lock);
2175 return -1;
2176 }
2177
2178 bind = &server_opts.binds[i];
2179
2180 if (set_addr) {
2181 port = bind->port;
2182 } else {
2183 address = bind->address;
2184 }
2185
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002186 if (!set_addr && (endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002187 ret = -1;
2188 goto cleanup;
2189 }
2190
2191 /* we have all the information we need to create a listening socket */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002192 if (address && (port || (endpt->ti == NC_TI_UNIX))) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002193 /* create new socket, close the old one */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002194 if (endpt->ti == NC_TI_UNIX) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002195 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002196 } else {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002197 sock = nc_sock_listen_inet(address, port, &endpt->ka);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002198 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002199 if (sock == -1) {
2200 ret = -1;
2201 goto cleanup;
2202 }
2203
2204 if (bind->sock > -1) {
2205 close(bind->sock);
2206 }
2207 bind->sock = sock;
2208 } /* else we are just setting address or port */
2209
2210 if (set_addr) {
2211 lydict_remove(server_opts.ctx, bind->address);
Michal Vasko77367452021-02-16 16:32:18 +01002212 lydict_insert(server_opts.ctx, address, 0, &bind->address);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002213 } else {
2214 bind->port = port;
2215 }
2216
2217 if (sock > -1) {
Michal Vasko946cacb2020-08-12 11:18:08 +02002218 switch (endpt->ti) {
2219 case NC_TI_UNIX:
2220 VRB("Listening on %s for UNIX connections.", address);
2221 break;
2222#ifdef NC_ENABLED_SSH
2223 case NC_TI_LIBSSH:
2224 VRB("Listening on %s:%u for SSH connections.", address, port);
2225 break;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002226#endif
Michal Vasko946cacb2020-08-12 11:18:08 +02002227#ifdef NC_ENABLED_TLS
2228 case NC_TI_OPENSSL:
2229 VRB("Listening on %s:%u for TLS connections.", address, port);
2230 break;
2231#endif
2232 default:
2233 ERRINT;
2234 break;
2235 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002236 }
2237
2238cleanup:
2239 /* ENDPT UNLOCK */
2240 pthread_rwlock_unlock(&server_opts.endpt_lock);
2241
2242 /* BIND UNLOCK */
2243 pthread_mutex_unlock(&server_opts.bind_lock);
2244
2245 return ret;
2246}
2247
2248API int
2249nc_server_endpt_set_address(const char *endpt_name, const char *address)
2250{
2251 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2252}
2253
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002254#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002255
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002256API int
2257nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2258{
2259 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2260}
2261
Michal Vasko946cacb2020-08-12 11:18:08 +02002262#endif
2263
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002264API int
2265nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2266{
2267 struct nc_endpt *endpt;
2268 uint16_t i;
2269 int ret = 0;
2270
2271 if (!endpt_name) {
2272 ERRARG("endpt_name");
2273 return -1;
2274 } else if (mode == 0) {
2275 ERRARG("mode");
2276 return -1;
2277 }
2278
2279 /* ENDPT LOCK */
2280 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002281 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002282 return -1;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002283 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002284
2285 if (endpt->ti != NC_TI_UNIX) {
2286 ret = -1;
2287 goto cleanup;
2288 }
2289
2290 endpt->opts.unixsock->mode = mode;
2291 endpt->opts.unixsock->uid = uid;
2292 endpt->opts.unixsock->gid = gid;
2293
2294cleanup:
2295 /* ENDPT UNLOCK */
2296 pthread_rwlock_unlock(&server_opts.endpt_lock);
2297
2298 return ret;
2299}
2300
2301API int
2302nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2303{
2304 struct nc_endpt *endpt;
2305 int ret = 0;
2306
2307 if (!endpt_name) {
2308 ERRARG("endpt_name");
2309 return -1;
2310 }
2311
2312 /* ENDPT LOCK */
2313 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2314 if (!endpt) {
2315 return -1;
2316 }
2317
2318 endpt->ka.enabled = (enable ? 1 : 0);
2319
2320 /* ENDPT UNLOCK */
2321 pthread_rwlock_unlock(&server_opts.endpt_lock);
2322
2323 return ret;
2324}
2325
2326API int
2327nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2328{
2329 struct nc_endpt *endpt;
2330 int ret = 0;
2331
2332 if (!endpt_name) {
2333 ERRARG("endpt_name");
2334 return -1;
2335 }
2336
2337 /* ENDPT LOCK */
2338 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2339 if (!endpt) {
2340 return -1;
2341 }
2342
2343 if (idle_time > -1) {
2344 endpt->ka.idle_time = idle_time;
2345 }
2346 if (max_probes > -1) {
2347 endpt->ka.max_probes = max_probes;
2348 }
2349 if (probe_interval > -1) {
2350 endpt->ka.probe_interval = probe_interval;
2351 }
2352
2353 /* ENDPT UNLOCK */
2354 pthread_rwlock_unlock(&server_opts.endpt_lock);
2355
2356 return ret;
2357}
2358
Michal Vasko71090fc2016-05-24 16:37:28 +02002359API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002360nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002361{
Michal Vasko71090fc2016-05-24 16:37:28 +02002362 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002363 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002364 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002365 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002366 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002367
Michal Vasko45e53ae2016-04-07 11:46:03 +02002368 if (!server_opts.ctx) {
2369 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002370 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002371 } else if (!session) {
2372 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002373 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002374 }
2375
Michal Vaskoade892d2017-02-22 13:40:35 +01002376 /* BIND LOCK */
2377 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002378
2379 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02002380 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002381 /* BIND UNLOCK */
2382 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002383 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002384 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002385
Michal Vaskoe2713da2016-08-22 16:06:40 +02002386 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002387 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002388 /* BIND UNLOCK */
2389 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002390 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002391 if (!ret) {
2392 return NC_MSG_WOULDBLOCK;
2393 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002394 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002395 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002396
2397 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2398 /* ENDPT READ LOCK */
2399 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2400
2401 /* BIND UNLOCK */
2402 pthread_mutex_unlock(&server_opts.bind_lock);
2403
Michal Vaskob48aa812016-01-18 14:13:09 +01002404 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002405
Michal Vasko131120a2018-05-29 15:44:02 +02002406 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002407 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002408 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002409 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002410 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002411 msgtype = NC_MSG_ERROR;
2412 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002413 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002414 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002415 (*session)->ctx = server_opts.ctx;
2416 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko77367452021-02-16 16:32:18 +01002417 lydict_insert_zc(server_opts.ctx, host, &(*session)->host);
Michal Vasko1a38c862016-01-15 15:50:07 +01002418 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002419
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002420 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002421#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002422 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2423 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002424 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002425 if (ret < 0) {
2426 msgtype = NC_MSG_ERROR;
2427 goto cleanup;
2428 } else if (!ret) {
2429 msgtype = NC_MSG_WOULDBLOCK;
2430 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002431 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002432 } else
2433#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002434#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002435 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2436 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002437 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002438 if (ret < 0) {
2439 msgtype = NC_MSG_ERROR;
2440 goto cleanup;
2441 } else if (!ret) {
2442 msgtype = NC_MSG_WOULDBLOCK;
2443 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002444 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002445 } else
2446#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002447 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2448 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2449 ret = nc_accept_unix(*session, sock);
2450 if (ret < 0) {
2451 msgtype = NC_MSG_ERROR;
2452 goto cleanup;
2453 }
2454 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002455 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002456 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002457 msgtype = NC_MSG_ERROR;
2458 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002459 }
2460
Michal Vasko2cc4c682016-03-01 09:16:48 +01002461 (*session)->data = NULL;
2462
Michal Vaskoade892d2017-02-22 13:40:35 +01002463 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002464 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002465
Michal Vaskob48aa812016-01-18 14:13:09 +01002466 /* assign new SID atomically */
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002467 (*session)->id = ATOMIC_INC(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002468
Michal Vasko9e036d52016-01-08 10:49:26 +01002469 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002470 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002471 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002472 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002473 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002474 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002475 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002476
2477 nc_gettimespec_mono(&ts_cur);
2478 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2479 nc_gettimespec_real(&ts_cur);
2480 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002481 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002482
Michal Vasko71090fc2016-05-24 16:37:28 +02002483 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002484
Michal Vasko71090fc2016-05-24 16:37:28 +02002485cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002486 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002487 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002488
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002489 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002490 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002491 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002492}
2493
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002494#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002495
Michal Vaskoadf30f02019-06-24 09:34:47 +02002496/* client is expected to be locked */
2497static int
2498_nc_server_ch_client_del_endpt(struct nc_ch_client *client, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002499{
2500 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002501 int ret = -1;
2502
2503 if (!endpt_name) {
2504 /* remove all endpoints */
2505 for (i = 0; i < client->ch_endpt_count; ++i) {
2506 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2507 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2508 if (client->ch_endpts[i].sock_pending != -1) {
2509 close(client->ch_endpts[i].sock_pending);
2510 }
2511 switch (client->ch_endpts[i].ti) {
2512#ifdef NC_ENABLED_SSH
2513 case NC_TI_LIBSSH:
2514 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2515 free(client->ch_endpts[i].opts.ssh);
2516 break;
2517#endif
2518#ifdef NC_ENABLED_TLS
2519 case NC_TI_OPENSSL:
2520 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2521 free(client->ch_endpts[i].opts.tls);
2522 break;
2523#endif
2524 default:
2525 ERRINT;
2526 /* won't get here ...*/
2527 break;
2528 }
2529 }
2530 free(client->ch_endpts);
2531 client->ch_endpts = NULL;
2532 client->ch_endpt_count = 0;
2533
2534 ret = 0;
2535 } else {
2536 for (i = 0; i < client->ch_endpt_count; ++i) {
2537 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
2538 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2539 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2540 if (client->ch_endpts[i].sock_pending != -1) {
2541 close(client->ch_endpts[i].sock_pending);
2542 }
2543 switch (client->ch_endpts[i].ti) {
2544#ifdef NC_ENABLED_SSH
2545 case NC_TI_LIBSSH:
2546 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2547 free(client->ch_endpts[i].opts.ssh);
2548 break;
2549#endif
2550#ifdef NC_ENABLED_TLS
2551 case NC_TI_OPENSSL:
2552 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2553 free(client->ch_endpts[i].opts.tls);
2554 break;
2555#endif
2556 default:
2557 ERRINT;
2558 /* won't get here ...*/
2559 break;
2560 }
2561
2562 /* move last endpoint to the empty space */
2563 --client->ch_endpt_count;
2564 if (i < client->ch_endpt_count) {
2565 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2566 } else if (!server_opts.ch_client_count) {
2567 free(server_opts.ch_clients);
2568 server_opts.ch_clients = NULL;
2569 }
2570
2571 ret = 0;
2572 break;
2573 }
2574 }
2575 }
2576
2577 return ret;
2578}
2579
2580API int
2581nc_server_ch_add_client(const char *name)
2582{
2583 uint16_t i;
2584 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002585
2586 if (!name) {
2587 ERRARG("name");
2588 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002589 }
2590
2591 /* WRITE LOCK */
2592 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2593
2594 /* check name uniqueness */
2595 for (i = 0; i < server_opts.ch_client_count; ++i) {
2596 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2597 ERR("Call Home client \"%s\" already exists.", name);
2598 /* WRITE UNLOCK */
2599 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2600 return -1;
2601 }
2602 }
2603
2604 ++server_opts.ch_client_count;
2605 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2606 if (!server_opts.ch_clients) {
2607 ERRMEM;
2608 /* WRITE UNLOCK */
2609 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2610 return -1;
2611 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002612 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002613
Michal Vasko77367452021-02-16 16:32:18 +01002614 lydict_insert(server_opts.ctx, name, 0, &client->name);
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02002615 client->id = ATOMIC_INC(server_opts.new_client_id);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002616 client->ch_endpts = NULL;
2617 client->ch_endpt_count = 0;
2618 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002619
Michal Vasko2e6defd2016-10-07 15:48:15 +02002620 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002621 client->start_with = NC_CH_FIRST_LISTED;
2622 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002623
Michal Vaskoadf30f02019-06-24 09:34:47 +02002624 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002625
2626 /* WRITE UNLOCK */
2627 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2628
2629 return 0;
2630}
2631
2632API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002633nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002634{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002635 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002636 int ret = -1;
2637
2638 /* WRITE LOCK */
2639 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2640
Michal Vaskoadf30f02019-06-24 09:34:47 +02002641 if (!name) {
2642 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002643 for (i = 0; i < server_opts.ch_client_count; ++i) {
2644 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2645
2646 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002647 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002648
2649 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002650 ret = 0;
2651 }
2652 free(server_opts.ch_clients);
2653 server_opts.ch_clients = NULL;
2654
2655 server_opts.ch_client_count = 0;
2656
2657 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002658 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002659 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002660 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002661 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2662
Michal Vasko2e6defd2016-10-07 15:48:15 +02002663 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002664 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002665
2666 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2667
2668 /* move last client and endpoint(s) to the empty space */
2669 --server_opts.ch_client_count;
2670 if (i < server_opts.ch_client_count) {
2671 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002672 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002673 } else if (!server_opts.ch_client_count) {
2674 free(server_opts.ch_clients);
2675 server_opts.ch_clients = NULL;
2676 }
2677
2678 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002679 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002680 }
2681 }
2682 }
2683
2684 /* WRITE UNLOCK */
2685 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2686
2687 return ret;
2688}
2689
2690API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002691nc_server_ch_is_client(const char *name)
2692{
2693 uint16_t i;
2694 int found = 0;
2695
2696 if (!name) {
2697 return found;
2698 }
2699
2700 /* READ LOCK */
2701 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2702
2703 /* check name uniqueness */
2704 for (i = 0; i < server_opts.ch_client_count; ++i) {
2705 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2706 found = 1;
2707 break;
2708 }
2709 }
2710
2711 /* UNLOCK */
2712 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2713
2714 return found;
2715}
2716
2717API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002718nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002719{
2720 uint16_t i;
2721 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002722 struct nc_ch_endpt *endpt;
2723 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002724
2725 if (!client_name) {
2726 ERRARG("client_name");
2727 return -1;
2728 } else if (!endpt_name) {
2729 ERRARG("endpt_name");
2730 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002731 } else if (!ti) {
2732 ERRARG("ti");
2733 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002734 }
2735
2736 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002737 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002738 if (!client) {
2739 return -1;
2740 }
2741
2742 for (i = 0; i < client->ch_endpt_count; ++i) {
2743 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2744 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002745 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002746 }
2747 }
2748
2749 ++client->ch_endpt_count;
2750 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2751 if (!client->ch_endpts) {
2752 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002753 goto cleanup;
2754 }
2755 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2756
2757 memset(endpt, 0, sizeof *client->ch_endpts);
Michal Vasko77367452021-02-16 16:32:18 +01002758 lydict_insert(server_opts.ctx, endpt_name, 0, &endpt->name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002759 endpt->ti = ti;
2760 endpt->sock_pending = -1;
2761 endpt->ka.idle_time = 1;
2762 endpt->ka.max_probes = 10;
2763 endpt->ka.probe_interval = 5;
2764
2765 switch (ti) {
2766#ifdef NC_ENABLED_SSH
2767 case NC_TI_LIBSSH:
2768 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2769 if (!endpt->opts.ssh) {
2770 ERRMEM;
2771 goto cleanup;
2772 }
2773 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2774 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002775 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002776 break;
2777#endif
2778#ifdef NC_ENABLED_TLS
2779 case NC_TI_OPENSSL:
2780 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2781 if (!endpt->opts.tls) {
2782 ERRMEM;
2783 goto cleanup;
2784 }
2785 break;
2786#endif
2787 default:
2788 ERRINT;
2789 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002790 }
2791
Michal Vaskoadf30f02019-06-24 09:34:47 +02002792 /* success */
2793 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002794
Michal Vaskoadf30f02019-06-24 09:34:47 +02002795cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002796 /* UNLOCK */
2797 nc_server_ch_client_unlock(client);
2798
Michal Vaskoadf30f02019-06-24 09:34:47 +02002799 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002800}
2801
2802API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002803nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002804{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002805 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002806 struct nc_ch_client *client;
2807
2808 if (!client_name) {
2809 ERRARG("client_name");
2810 return -1;
2811 }
2812
2813 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002814 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002815 if (!client) {
2816 return -1;
2817 }
2818
Michal Vaskoadf30f02019-06-24 09:34:47 +02002819 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002820
2821 /* UNLOCK */
2822 nc_server_ch_client_unlock(client);
2823
2824 return ret;
2825}
2826
2827API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002828nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2829{
2830 uint16_t i;
2831 struct nc_ch_client *client = NULL;
2832 int found = 0;
2833
2834 if (!client_name || !endpt_name) {
2835 return found;
2836 }
2837
2838 /* READ LOCK */
2839 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2840
2841 for (i = 0; i < server_opts.ch_client_count; ++i) {
2842 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2843 client = &server_opts.ch_clients[i];
2844 break;
2845 }
2846 }
2847
2848 if (!client) {
2849 goto cleanup;
2850 }
2851
2852 for (i = 0; i < client->ch_endpt_count; ++i) {
2853 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2854 found = 1;
2855 break;
2856 }
2857 }
2858
2859cleanup:
2860 /* UNLOCK */
2861 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2862 return found;
2863}
2864
2865API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002866nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2867{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002868 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002869 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002870
2871 if (!client_name) {
2872 ERRARG("client_name");
2873 return -1;
2874 } else if (!endpt_name) {
2875 ERRARG("endpt_name");
2876 return -1;
2877 } else if (!address) {
2878 ERRARG("address");
2879 return -1;
2880 }
2881
2882 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002883 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2884 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002885 return -1;
2886 }
2887
Michal Vaskoadf30f02019-06-24 09:34:47 +02002888 lydict_remove(server_opts.ctx, endpt->address);
Michal Vasko77367452021-02-16 16:32:18 +01002889 lydict_insert(server_opts.ctx, address, 0, &endpt->address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002890
2891 /* UNLOCK */
2892 nc_server_ch_client_unlock(client);
2893
Michal Vaskoadf30f02019-06-24 09:34:47 +02002894 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002895}
2896
2897API int
2898nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2899{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002900 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002901 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002902
2903 if (!client_name) {
2904 ERRARG("client_name");
2905 return -1;
2906 } else if (!endpt_name) {
2907 ERRARG("endpt_name");
2908 return -1;
2909 } else if (!port) {
2910 ERRARG("port");
2911 return -1;
2912 }
2913
2914 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002915 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2916 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002917 return -1;
2918 }
2919
Michal Vaskoadf30f02019-06-24 09:34:47 +02002920 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002921
2922 /* UNLOCK */
2923 nc_server_ch_client_unlock(client);
2924
ravsz5c5a4422020-03-31 15:53:21 +02002925 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002926}
2927
2928API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002929nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
2930{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002931 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002932 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002933
2934 if (!client_name) {
2935 ERRARG("client_name");
2936 return -1;
2937 } else if (!endpt_name) {
2938 ERRARG("endpt_name");
2939 return -1;
2940 }
2941
2942 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002943 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2944 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002945 return -1;
2946 }
2947
Michal Vaskoadf30f02019-06-24 09:34:47 +02002948 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002949
2950 /* UNLOCK */
2951 nc_server_ch_client_unlock(client);
2952
Michal Vasko9af829a2019-09-12 13:50:00 +02002953 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002954}
2955
2956API int
2957nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
2958 int probe_interval)
2959{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002960 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002961 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002962
2963 if (!client_name) {
2964 ERRARG("client_name");
2965 return -1;
2966 } else if (!endpt_name) {
2967 ERRARG("endpt_name");
2968 return -1;
2969 }
2970
2971 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002972 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2973 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002974 return -1;
2975 }
2976
Michal Vaskoadf30f02019-06-24 09:34:47 +02002977 if (idle_time > -1) {
2978 endpt->ka.idle_time = idle_time;
2979 }
2980 if (max_probes > -1) {
2981 endpt->ka.max_probes = max_probes;
2982 }
2983 if (probe_interval > -1) {
2984 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002985 }
2986
2987 /* UNLOCK */
2988 nc_server_ch_client_unlock(client);
2989
Michal Vasko9af829a2019-09-12 13:50:00 +02002990 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002991}
2992
2993API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002994nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2995{
2996 struct nc_ch_client *client;
2997
2998 if (!client_name) {
2999 ERRARG("client_name");
3000 return -1;
3001 } else if (!conn_type) {
3002 ERRARG("conn_type");
3003 return -1;
3004 }
3005
3006 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003007 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003008 if (!client) {
3009 return -1;
3010 }
3011
3012 if (client->conn_type != conn_type) {
3013 client->conn_type = conn_type;
3014
3015 /* set default options */
3016 switch (conn_type) {
3017 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003018 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003019 break;
3020 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003021 client->conn.period.period = 60;
3022 client->conn.period.anchor_time = 0;
3023 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003024 break;
3025 default:
3026 ERRINT;
3027 break;
3028 }
3029 }
3030
3031 /* UNLOCK */
3032 nc_server_ch_client_unlock(client);
3033
3034 return 0;
3035}
3036
3037API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003038nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
3039{
3040 struct nc_ch_client *client;
3041
3042 if (!client_name) {
3043 ERRARG("client_name");
3044 return -1;
3045 } else if (!period) {
3046 ERRARG("period");
3047 return -1;
3048 }
3049
3050 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003051 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003052 if (!client) {
3053 return -1;
3054 }
3055
3056 if (client->conn_type != NC_CH_PERIOD) {
ravsz20789e12020-03-31 14:43:05 +02003057 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003058 /* UNLOCK */
3059 nc_server_ch_client_unlock(client);
3060 return -1;
3061 }
3062
3063 client->conn.period.period = period;
3064
3065 /* UNLOCK */
3066 nc_server_ch_client_unlock(client);
3067
3068 return 0;
3069}
3070
3071API int
3072nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003073{
3074 struct nc_ch_client *client;
3075
3076 if (!client_name) {
3077 ERRARG("client_name");
3078 return -1;
3079 }
3080
3081 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003082 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003083 if (!client) {
3084 return -1;
3085 }
3086
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003087 if (client->conn_type != NC_CH_PERIOD) {
ravsz20789e12020-03-31 14:43:05 +02003088 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003089 /* UNLOCK */
3090 nc_server_ch_client_unlock(client);
3091 return -1;
3092 }
3093
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003094 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003095
3096 /* UNLOCK */
3097 nc_server_ch_client_unlock(client);
3098
3099 return 0;
3100}
3101
3102API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003103nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003104{
3105 struct nc_ch_client *client;
3106
3107 if (!client_name) {
3108 ERRARG("client_name");
3109 return -1;
3110 }
3111
3112 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003113 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003114 if (!client) {
3115 return -1;
3116 }
3117
3118 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10003119 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003120 /* UNLOCK */
3121 nc_server_ch_client_unlock(client);
3122 return -1;
3123 }
3124
3125 client->conn.period.idle_timeout = idle_timeout;
3126
3127 /* UNLOCK */
3128 nc_server_ch_client_unlock(client);
3129
3130 return 0;
3131}
3132
3133API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003134nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
3135{
3136 struct nc_ch_client *client;
3137
3138 if (!client_name) {
3139 ERRARG("client_name");
3140 return -1;
3141 }
3142
3143 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003144 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003145 if (!client) {
3146 return -1;
3147 }
3148
3149 client->start_with = start_with;
3150
3151 /* UNLOCK */
3152 nc_server_ch_client_unlock(client);
3153
3154 return 0;
3155}
3156
3157API int
3158nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
3159{
3160 struct nc_ch_client *client;
3161
3162 if (!client_name) {
3163 ERRARG("client_name");
3164 return -1;
3165 } else if (!max_attempts) {
3166 ERRARG("max_attempts");
3167 return -1;
3168 }
3169
3170 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003171 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003172 if (!client) {
3173 return -1;
3174 }
3175
3176 client->max_attempts = max_attempts;
3177
3178 /* UNLOCK */
3179 nc_server_ch_client_unlock(client);
3180
3181 return 0;
3182}
3183
3184/* client lock is expected to be held */
3185static NC_MSG_TYPE
Michal Vaskoadf30f02019-06-24 09:34:47 +02003186nc_connect_ch_endpt(struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01003187{
Michal Vasko71090fc2016-05-24 16:37:28 +02003188 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003189 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02003190 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01003191 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01003192
Michal Vasko4c612cd2021-02-05 08:53:42 +01003193 sock = nc_sock_connect(endpt->address, endpt->port, NC_SOCKET_CH_TIMEOUT, &endpt->ka, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01003194 if (sock < 0) {
Michal Vasko0b30e452021-03-03 10:30:15 +01003195 if (endpt->sock_pending > -1) {
3196 ++endpt->sock_retries;
3197 if (endpt->sock_retries == NC_SOCKET_CH_RETRIES) {
3198 ERR("Failed to connect socket %d after %d retries, closing.", endpt->sock_pending, NC_SOCKET_CH_RETRIES);
3199 close(endpt->sock_pending);
3200 endpt->sock_pending = -1;
3201 endpt->sock_retries = 0;
3202 }
Michal Vasko4c612cd2021-02-05 08:53:42 +01003203 }
Michal Vasko71090fc2016-05-24 16:37:28 +02003204 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003205 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00003206 /* no need to store the socket as pending any longer */
3207 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01003208
Michal Vasko131120a2018-05-29 15:44:02 +02003209 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01003210 if (!(*session)) {
3211 ERRMEM;
3212 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01003213 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02003214 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003215 }
3216 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01003217 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003218 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko77367452021-02-16 16:32:18 +01003219 lydict_insert_zc(server_opts.ctx, ip_host, &(*session)->host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003220 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01003221
Michal Vaskob05053d2016-01-22 16:12:06 +01003222 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01003223#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02003224 if (endpt->ti == NC_TI_LIBSSH) {
3225 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01003226 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003227 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003228
Michal Vasko71090fc2016-05-24 16:37:28 +02003229 if (ret < 0) {
3230 msgtype = NC_MSG_ERROR;
3231 goto fail;
3232 } else if (!ret) {
3233 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003234 goto fail;
3235 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003236 } else
3237#endif
Radek Krejci53691be2016-02-22 13:58:37 +01003238#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02003239 if (endpt->ti == NC_TI_OPENSSL) {
3240 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01003241 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003242 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003243
Michal Vasko71090fc2016-05-24 16:37:28 +02003244 if (ret < 0) {
3245 msgtype = NC_MSG_ERROR;
3246 goto fail;
3247 } else if (!ret) {
3248 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003249 goto fail;
3250 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003251 } else
3252#endif
3253 {
Michal Vaskob05053d2016-01-22 16:12:06 +01003254 ERRINT;
3255 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003256 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003257 goto fail;
3258 }
3259
3260 /* assign new SID atomically */
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02003261 (*session)->id = ATOMIC_INC(server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003262
3263 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003264 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003265 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003266 goto fail;
3267 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003268
3269 nc_gettimespec_mono(&ts_cur);
3270 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
3271 nc_gettimespec_real(&ts_cur);
3272 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003273 (*session)->status = NC_STATUS_RUNNING;
3274
Michal Vasko71090fc2016-05-24 16:37:28 +02003275 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003276
3277fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003278 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003279 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02003280 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003281}
3282
Michal Vasko2e6defd2016-10-07 15:48:15 +02003283struct nc_ch_client_thread_arg {
3284 char *client_name;
Michal Vaskof1c26c22021-04-12 16:34:33 +02003285 int (*session_clb)(const char *client_name, struct nc_session *new_session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003286};
3287
3288static struct nc_ch_client *
3289nc_server_ch_client_with_endpt_lock(const char *name)
3290{
3291 struct nc_ch_client *client;
3292
3293 while (1) {
3294 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003295 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003296 if (!client) {
3297 return NULL;
3298 }
3299 if (client->ch_endpt_count) {
3300 return client;
3301 }
3302 /* no endpoints defined yet */
3303
3304 /* UNLOCK */
3305 nc_server_ch_client_unlock(client);
3306
3307 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3308 }
3309
3310 return NULL;
3311}
3312
3313static int
3314nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3315{
Michal Vasko3f05a092018-03-13 10:39:49 +01003316 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003317 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003318 struct timespec ts;
3319 struct nc_ch_client *client;
3320
Michal Vasko2e6defd2016-10-07 15:48:15 +02003321 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003322 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003323
Michal Vasko0db3db52021-03-03 10:45:42 +01003324 session->flags |= NC_SESSION_CALLHOME;
3325
Michal Vasko2e6defd2016-10-07 15:48:15 +02003326 /* give the session to the user */
Michal Vaskof1c26c22021-04-12 16:34:33 +02003327 if (data->session_clb(data->client_name, session)) {
3328 /* something is wrong, free the session */
3329 session->flags &= ~NC_SESSION_CALLHOME;
3330
3331 /* CH UNLOCK */
3332 pthread_mutex_unlock(&session->opts.server.ch_lock);
3333
3334 nc_session_free(session, NULL);
3335 return 0;
3336 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003337
3338 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003339 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003340 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003341
Michal Vasko0db3db52021-03-03 10:45:42 +01003342 /* CH COND WAIT */
Michal Vaskoacf98472021-02-04 15:33:57 +01003343 r = pthread_cond_timedwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, &ts);
Michal Vasko3f05a092018-03-13 10:39:49 +01003344 if (!r) {
3345 /* we were woken up, something probably happened */
3346 if (session->status != NC_STATUS_RUNNING) {
3347 break;
3348 }
3349 } else if (r != ETIMEDOUT) {
3350 ERR("Pthread condition timedwait failed (%s).", strerror(r));
3351 ret = -1;
3352 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003353 }
3354
Michal Vasko2e6defd2016-10-07 15:48:15 +02003355 /* check whether the client was not removed */
3356 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003357 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003358 if (!client) {
3359 /* client was removed, finish thread */
3360 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003361 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003362 ret = 1;
3363 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003364 }
3365
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003366 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003367 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003368 } else {
3369 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003370 }
3371
Michal Vasko9fb42272017-10-05 13:50:05 +02003372 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003373 if (!session->opts.server.ntf_status && idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003374 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
3375 session->status = NC_STATUS_INVALID;
3376 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3377 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003378
3379 /* UNLOCK */
3380 nc_server_ch_client_unlock(client);
3381
3382 } while (session->status == NC_STATUS_RUNNING);
3383
Michal Vasko0db3db52021-03-03 10:45:42 +01003384 /* signal to nc_session_free() that CH registered this session not being valid anymore */
3385 session->flags &= ~NC_SESSION_CALLHOME;
3386
Michal Vasko27377422018-03-15 08:59:35 +01003387 /* CH UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003388 pthread_mutex_unlock(&session->opts.server.ch_lock);
Michal Vasko27377422018-03-15 08:59:35 +01003389
Michal Vasko3f05a092018-03-13 10:39:49 +01003390 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003391}
3392
3393static void *
3394nc_ch_client_thread(void *arg)
3395{
3396 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3397 NC_MSG_TYPE msgtype;
3398 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003399 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003400 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003401 struct nc_ch_endpt *cur_endpt;
3402 struct nc_session *session;
3403 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003404 uint32_t client_id;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003405 time_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003406
3407 /* LOCK */
3408 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3409 if (!client) {
3410 goto cleanup;
3411 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003412 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003413
3414 cur_endpt = &client->ch_endpts[0];
3415 cur_endpt_name = strdup(cur_endpt->name);
3416
Michal Vasko29af44b2016-10-13 10:59:55 +02003417 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003418 while (1) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02003419 msgtype = nc_connect_ch_endpt(cur_endpt, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003420
3421 if (msgtype == NC_MSG_HELLO) {
3422 /* UNLOCK */
3423 nc_server_ch_client_unlock(client);
3424
Michal Vasko29af44b2016-10-13 10:59:55 +02003425 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003426 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3427 goto cleanup;
3428 }
Michal Vasko0a047f02021-03-03 10:30:52 +01003429 VRB("Call Home client \"%s\" session terminated.", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003430
3431 /* LOCK */
3432 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3433 if (!client) {
3434 goto cleanup;
3435 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003436 if (client->id != client_id) {
3437 nc_server_ch_client_unlock(client);
3438 goto cleanup;
3439 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003440
3441 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003442 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003443 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003444 /* UNLOCK */
3445 nc_server_ch_client_unlock(client);
3446
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003447 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
3448 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
Michal Vasko0a047f02021-03-03 10:30:52 +01003449 VRB("Call Home client \"%s\" reconnecting in %d seconds.", data->client_name, reconnect_in);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003450 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003451
3452 /* LOCK */
3453 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3454 if (!client) {
3455 goto cleanup;
3456 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003457 if (client->id != client_id) {
3458 nc_server_ch_client_unlock(client);
3459 goto cleanup;
3460 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003461 }
3462
3463 /* set next endpoint to try */
3464 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003465 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003466 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003467 /* we keep the current one but due to unlock/lock we have to find it again */
3468 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3469 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3470 break;
3471 }
3472 }
3473 if (next_endpt_index >= client->ch_endpt_count) {
3474 /* endpoint was removed, start with the first one */
3475 next_endpt_index = 0;
3476 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003477 } else {
3478 /* just get a random index */
3479 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003480 }
3481
Michal Vasko2e6defd2016-10-07 15:48:15 +02003482 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003483 /* UNLOCK */
3484 nc_server_ch_client_unlock(client);
3485
Michal Vasko2e6defd2016-10-07 15:48:15 +02003486 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003487 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3488
Michal Vasko6bb116b2016-10-26 13:53:46 +02003489 /* LOCK */
3490 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3491 if (!client) {
3492 goto cleanup;
3493 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003494 if (client->id != client_id) {
3495 nc_server_ch_client_unlock(client);
3496 goto cleanup;
3497 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003498
Michal Vasko2e6defd2016-10-07 15:48:15 +02003499 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003500
3501 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003502 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3503 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003504 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003505 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003506 }
3507
Peter Feiged05f2252018-09-03 08:09:47 +00003508 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003509 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003510 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003511 cur_attempts = 0;
3512 } else if (cur_attempts == client->max_attempts) {
3513 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003514 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003515 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003516 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003517 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003518 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003519 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003520 }
3521
3522 cur_attempts = 0;
3523 } /* else we keep the current one */
3524 }
Peter Feiged05f2252018-09-03 08:09:47 +00003525
3526 cur_endpt = &client->ch_endpts[next_endpt_index];
3527 free(cur_endpt_name);
3528 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003529 }
3530
3531cleanup:
3532 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003533 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003534 free(data->client_name);
3535 free(data);
3536 return NULL;
3537}
3538
3539API int
Michal Vaskof1c26c22021-04-12 16:34:33 +02003540nc_connect_ch_client_dispatch(const char *client_name, int (*session_clb)(const char *client_name,
Michal Vaskoadf30f02019-06-24 09:34:47 +02003541 struct nc_session *new_session))
Michal Vasko3f05a092018-03-13 10:39:49 +01003542{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003543 int ret;
3544 pthread_t tid;
3545 struct nc_ch_client_thread_arg *arg;
3546
3547 if (!client_name) {
3548 ERRARG("client_name");
3549 return -1;
3550 } else if (!session_clb) {
3551 ERRARG("session_clb");
3552 return -1;
3553 }
3554
3555 arg = malloc(sizeof *arg);
3556 if (!arg) {
3557 ERRMEM;
3558 return -1;
3559 }
3560 arg->client_name = strdup(client_name);
3561 if (!arg->client_name) {
3562 ERRMEM;
3563 free(arg);
3564 return -1;
3565 }
3566 arg->session_clb = session_clb;
3567
3568 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3569 if (ret) {
3570 ERR("Creating a new thread failed (%s).", strerror(ret));
3571 free(arg->client_name);
3572 free(arg);
3573 return -1;
3574 }
3575 /* the thread now manages arg */
3576
3577 pthread_detach(tid);
3578
3579 return 0;
3580}
3581
Radek Krejci53691be2016-02-22 13:58:37 +01003582#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003583
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003584API time_t
3585nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003586{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003587 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003588 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003589 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003590 }
3591
Michal Vasko2e6defd2016-10-07 15:48:15 +02003592 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003593}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003594
3595API void
Michal Vasko71dbd772021-03-23 14:08:37 +01003596nc_session_inc_notif_status(struct nc_session *session)
Michal Vasko3486a7c2017-03-03 13:28:07 +01003597{
3598 if (!session || (session->side != NC_SERVER)) {
3599 ERRARG("session");
3600 return;
3601 }
3602
Michal Vasko71dbd772021-03-23 14:08:37 +01003603 ++session->opts.server.ntf_status;
3604}
3605
3606API void
3607nc_session_dec_notif_status(struct nc_session *session)
3608{
3609 if (!session || (session->side != NC_SERVER)) {
3610 ERRARG("session");
3611 return;
3612 }
3613
3614 if (session->opts.server.ntf_status) {
3615 --session->opts.server.ntf_status;
3616 }
Michal Vasko3486a7c2017-03-03 13:28:07 +01003617}
3618
3619API int
3620nc_session_get_notif_status(const struct nc_session *session)
3621{
3622 if (!session || (session->side != NC_SERVER)) {
3623 ERRARG("session");
3624 return 0;
3625 }
3626
3627 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003628}
Michal Vasko8f430592019-02-26 08:32:54 +01003629
3630API int
3631nc_session_is_callhome(const struct nc_session *session)
3632{
3633 if (!session || (session->side != NC_SERVER)) {
3634 ERRARG("session");
3635 return 0;
3636 }
3637
3638 if (session->flags & NC_SESSION_CALLHOME) {
3639 return 1;
3640 }
3641
3642 return 0;
3643}