blob: 4ece29ca102be977cff9daa98500d30e646f3c75 [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) {
Michal Vasko05532772021-06-03 12:12:38 +020073 ERR(NULL, "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) {
Michal Vasko530d95c2021-05-28 13:32:02 +0200111 if ((!endpt_name || !strcmp(client->ch_endpts[j].name, endpt_name)) &&
112 (!ti || (ti == client->ch_endpts[j].ti))) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200113 endpt = &client->ch_endpts[j];
114 break;
115 }
116 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200117 break;
118 }
119 }
120
121 if (!client) {
Michal Vasko05532772021-06-03 12:12:38 +0200122 ERR(NULL, "Call Home client \"%s\" was not found.", name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200123
Michal Vasko2e6defd2016-10-07 15:48:15 +0200124 /* READ UNLOCK */
125 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200126 } else if (endpt_name && ti && !endpt) {
Michal Vasko05532772021-06-03 12:12:38 +0200127 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" was not found.", name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200128
129 /* READ UNLOCK */
130 pthread_rwlock_unlock(&server_opts.ch_client_lock);
131 } else {
132 /* CH CLIENT LOCK */
133 pthread_mutex_lock(&client->lock);
134
135 *client_p = client;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200136 }
137
Michal Vaskoadf30f02019-06-24 09:34:47 +0200138 return endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200139}
140
141void
142nc_server_ch_client_unlock(struct nc_ch_client *client)
143{
144 /* CH CLIENT UNLOCK */
145 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100146
147 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200148 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100149}
Michal Vasko086311b2016-01-08 09:53:11 +0100150
Michal Vasko1a38c862016-01-15 15:50:07 +0100151API void
152nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
153{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200154 if (!session) {
155 ERRARG("session");
156 return;
157 } else if (!reason) {
158 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100159 return;
160 }
161
Michal Vasko142cfea2017-08-07 10:12:11 +0200162 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
163 session->killed_by = 0;
164 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100165 session->term_reason = reason;
166}
167
Michal Vasko142cfea2017-08-07 10:12:11 +0200168API void
169nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
170{
171 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
172 ERRARG("session");
173 return;
174 } else if (!sid) {
175 ERRARG("sid");
176 return;
177 }
178
179 session->killed_by = sid;
180}
181
182API void
183nc_session_set_status(struct nc_session *session, NC_STATUS status)
184{
185 if (!session) {
186 ERRARG("session");
187 return;
188 } else if (!status) {
189 ERRARG("status");
190 return;
191 }
192
193 session->status = status;
194}
195
Michal Vasko086311b2016-01-08 09:53:11 +0100196int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200197nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100198{
Michal Vasko06c860d2018-07-09 16:08:52 +0200199 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100200 int is_ipv4, sock;
201 struct sockaddr_storage saddr;
202
203 struct sockaddr_in *saddr4;
204 struct sockaddr_in6 *saddr6;
205
Michal Vasko086311b2016-01-08 09:53:11 +0100206 if (!strchr(address, ':')) {
207 is_ipv4 = 1;
208 } else {
209 is_ipv4 = 0;
210 }
211
212 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
213 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200214 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100215 goto fail;
216 }
217
Michal Vaskobe52dc22018-10-17 09:28:17 +0200218 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200219 opt = 1;
220 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200221 ERR(NULL, "Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko06c860d2018-07-09 16:08:52 +0200222 goto fail;
223 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100224 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200225 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vasko83ad17e2019-01-30 10:11:37 +0100226 goto fail;
227 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200228
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200229 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100230 goto fail;
231 }
232
Michal Vaskof22d5ff2020-04-15 11:10:27 +0200233 memset(&saddr, 0, sizeof(struct sockaddr_storage));
Michal Vasko086311b2016-01-08 09:53:11 +0100234 if (is_ipv4) {
235 saddr4 = (struct sockaddr_in *)&saddr;
236
237 saddr4->sin_family = AF_INET;
238 saddr4->sin_port = htons(port);
239
240 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200241 ERR(NULL, "Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100242 goto fail;
243 }
244
245 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200246 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100247 goto fail;
248 }
249
250 } else {
251 saddr6 = (struct sockaddr_in6 *)&saddr;
252
253 saddr6->sin6_family = AF_INET6;
254 saddr6->sin6_port = htons(port);
255
256 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200257 ERR(NULL, "Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100258 goto fail;
259 }
260
261 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200262 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100263 goto fail;
264 }
265 }
266
Michal Vaskofb89d772016-01-08 12:25:35 +0100267 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200268 ERR(NULL, "Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100269 goto fail;
270 }
271
272 return sock;
273
274fail:
275 if (sock > -1) {
276 close(sock);
277 }
278
279 return -1;
280}
281
282int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200283nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
284{
285 struct sockaddr_un sun;
286 int sock = -1;
287
288 sock = socket(AF_UNIX, SOCK_STREAM, 0);
289 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200290 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200291 goto fail;
292 }
293
294 memset(&sun, 0, sizeof(sun));
295 sun.sun_family = AF_UNIX;
296 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
297
298 unlink(sun.sun_path);
299 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200300 ERR(NULL, "Could not bind \"%s\" (%s).", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200301 goto fail;
302 }
303
304 if (opts->mode != (mode_t)-1) {
305 if (chmod(sun.sun_path, opts->mode) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200306 ERR(NULL, "Failed to set unix socket permissions (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200307 goto fail;
308 }
309 }
310
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200311 if ((opts->uid != (uid_t)-1) || (opts->gid != (gid_t)-1)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200312 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200313 ERR(NULL, "Failed to set unix socket uid/gid (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200314 goto fail;
315 }
316 }
317
318 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200319 ERR(NULL, "Unable to start listening on \"%s\" (%s).", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200320 goto fail;
321 }
322
323 return sock;
324
325fail:
326 if (sock > -1) {
327 close(sock);
328 }
329
330 return -1;
331}
332
aPiecek90ff0242021-02-14 14:58:01 +0100333/**
334 * @brief Evaluate socket name for AF_UNIX socket.
335 * @param[in] acc_sock_fd is file descriptor for the accepted socket (a nonnegative).
336 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
337 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
338 * @return 0 if the stream socket is unnamed. Parameter host is set to NULL.
339 * @return -1 in case of error. Parameter host is set to NULL.
340 */
341static int
342sock_host_unix(int acc_sock_fd, char **host)
343{
344 char *sun_path;
345 struct sockaddr_storage saddr;
346 socklen_t addr_len;
347
348 *host = NULL;
349 saddr.ss_family = AF_UNIX;
350 addr_len = sizeof(saddr);
351
352 if (getsockname(acc_sock_fd, (struct sockaddr *)&saddr, &addr_len)) {
Michal Vasko05532772021-06-03 12:12:38 +0200353 ERR(NULL, "getsockname failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100354 return -1;
355 }
356
357 sun_path = ((struct sockaddr_un *)&saddr)->sun_path;
358 if (!sun_path) {
359 /* stream socket is unnamed */
360 return 0;
361 }
362
363 if (!(*host = strdup(sun_path))) {
364 ERRMEM;
365 return -1;
366 }
367
368 return 0;
369}
370
371/**
372 * @brief Evaluate socket name and port number for AF_INET socket.
373 * @param[in] addr is pointing to structure filled by accept function which was successful.
374 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
375 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
376 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
377 * @return -1 in case of error. Parameter host is set to NULL and port is unchanged.
378 */
379static int
380sock_host_inet(const struct sockaddr_in *addr, char **host, uint16_t *port)
381{
382 *host = malloc(INET_ADDRSTRLEN);
383 if (!(*host)) {
384 ERRMEM;
385 return -1;
386 }
387
aPiecek3da9b342021-02-18 15:00:03 +0100388 if (!inet_ntop(AF_INET, &addr->sin_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +0200389 ERR(NULL, "inet_ntop failed(%s).");
aPiecek90ff0242021-02-14 14:58:01 +0100390 free(*host);
391 *host = NULL;
392 return -1;
393 }
394
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200395 *port = ntohs(addr->sin_port);
aPiecek90ff0242021-02-14 14:58:01 +0100396
397 return 0;
398}
399
400/**
401 * @brief Evaluate socket name and port number for AF_INET6 socket.
402 * @param[in] addr is pointing to structure filled by accept function which was successful.
403 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
404 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
405 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
406 * @return -1 in case of error. Parameter host is set to the NULL and port is unchanged.
407 */
408static int
409sock_host_inet6(const struct sockaddr_in6 *addr, char **host, uint16_t *port)
410{
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200411 *host = malloc(INET6_ADDRSTRLEN);
aPiecek90ff0242021-02-14 14:58:01 +0100412 if (!(*host)) {
413 ERRMEM;
414 return -1;
415 }
416
aPiecek3da9b342021-02-18 15:00:03 +0100417 if (!inet_ntop(AF_INET6, &addr->sin6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +0200418 ERR(NULL, "inet_ntop failed(%s).");
aPiecek90ff0242021-02-14 14:58:01 +0100419 free(*host);
420 *host = NULL;
421 return -1;
422 }
423
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200424 *port = ntohs(addr->sin6_port);
aPiecek90ff0242021-02-14 14:58:01 +0100425
426 return 0;
427}
428
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200429int
Michal Vasko3031aae2016-01-27 16:07:18 +0100430nc_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 +0100431{
Michal Vaskof54cd352017-02-22 13:42:02 +0100432 sigset_t sigmask, origmask;
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200433 uint16_t i, j, pfd_count, client_port;
434 char *client_address;
Michal Vasko086311b2016-01-08 09:53:11 +0100435 struct pollfd *pfd;
436 struct sockaddr_storage saddr;
437 socklen_t saddr_len = sizeof(saddr);
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200438 int ret, client_sock, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100439
440 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100441 if (!pfd) {
442 ERRMEM;
443 return -1;
444 }
445
Michal Vaskoac2f6182017-01-30 14:32:03 +0100446 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200447 if (binds[i].sock < 0) {
448 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200449 continue;
450 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200451 if (binds[i].pollin) {
452 binds[i].pollin = 0;
453 /* leftover pollin */
454 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100455 break;
456 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100457 pfd[pfd_count].fd = binds[i].sock;
458 pfd[pfd_count].events = POLLIN;
459 pfd[pfd_count].revents = 0;
460
461 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100462 }
463
Michal Vasko0a3f3752016-10-13 14:58:38 +0200464 if (sock == -1) {
465 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100466 sigfillset(&sigmask);
467 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100468 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100469 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
470
Michal Vasko0a3f3752016-10-13 14:58:38 +0200471 if (!ret) {
472 /* we timeouted */
473 free(pfd);
474 return 0;
475 } else if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200476 ERR(NULL, "Poll failed (%s).", strerror(errno));
Michal Vasko0a3f3752016-10-13 14:58:38 +0200477 free(pfd);
478 return -1;
479 }
Michal Vasko086311b2016-01-08 09:53:11 +0100480
Michal Vaskoac2f6182017-01-30 14:32:03 +0100481 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
482 /* adjust i so that indices in binds and pfd always match */
483 while (binds[i].sock != pfd[j].fd) {
484 ++i;
485 }
486
487 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200488 --ret;
489
490 if (!ret) {
491 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100492 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200493 break;
494 } else {
495 /* just remember the event for next time */
496 binds[i].pollin = 1;
497 }
498 }
Michal Vasko086311b2016-01-08 09:53:11 +0100499 }
500 }
501 free(pfd);
502
503 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100504 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100505 return -1;
506 }
507
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200508 /* accept connection */
509 client_sock = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
510 if (client_sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200511 ERR(NULL, "Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100512 return -1;
513 }
514
Michal Vasko0190bc32016-03-02 15:47:49 +0100515 /* make the socket non-blocking */
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200516 if (((flags = fcntl(client_sock, F_GETFL)) == -1) || (fcntl(client_sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200517 ERR(NULL, "Fcntl failed (%s).", strerror(errno));
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200518 goto fail;
Michal Vasko0190bc32016-03-02 15:47:49 +0100519 }
520
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200521 /* learn information about the client end */
522 if (saddr.ss_family == AF_UNIX) {
523 if (sock_host_unix(client_sock, &client_address)) {
524 goto fail;
525 }
526 client_port = 0;
527 } else if (saddr.ss_family == AF_INET) {
528 if (sock_host_inet((struct sockaddr_in *)&saddr, &client_address, &client_port)) {
529 goto fail;
530 }
531 } else if (saddr.ss_family == AF_INET6) {
532 if (sock_host_inet6((struct sockaddr_in6 *)&saddr, &client_address, &client_port)) {
533 goto fail;
534 }
535 } else {
536 ERR(NULL, "Source host of an unknown protocol family.");
537 goto fail;
aPiecek90ff0242021-02-14 14:58:01 +0100538 }
Michal Vasko086311b2016-01-08 09:53:11 +0100539
aPiecek90ff0242021-02-14 14:58:01 +0100540 if (saddr.ss_family == AF_UNIX) {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200541 VRB(NULL, "Accepted a connection on %s.", binds[i].address);
aPiecek90ff0242021-02-14 14:58:01 +0100542 } else {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200543 VRB(NULL, "Accepted a connection on %s:%u from %s:%u.", binds[i].address, binds[i].port, client_address, client_port);
Michal Vasko086311b2016-01-08 09:53:11 +0100544 }
545
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200546 if (host) {
547 *host = client_address;
548 } else {
549 free(client_address);
550 }
551 if (port) {
552 *port = client_port;
553 }
554 if (idx) {
555 *idx = i;
556 }
557 return client_sock;
558
559fail:
560 close(client_sock);
561 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100562}
563
Michal Vasko05ba9df2016-01-13 14:40:27 +0100564static struct nc_server_reply *
Michal Vasko05532772021-06-03 12:12:38 +0200565nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100566{
Michal Vasko77367452021-02-16 16:32:18 +0100567 const char *identifier = NULL, *revision = NULL, *format = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100568 char *model_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100569 struct ly_out *out;
Michal Vasko9b1a9522021-03-15 16:24:26 +0100570 const struct lys_module *module = NULL, *mod;
Michal Vasko77367452021-02-16 16:32:18 +0100571 const struct lysp_submodule *submodule = NULL;
572 struct lyd_node *child, *err, *data = NULL;
573 LYS_OUTFORMAT outformat = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100574
Michal Vasko77367452021-02-16 16:32:18 +0100575 LY_LIST_FOR(lyd_child(rpc), child) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100576 if (!strcmp(child->schema->name, "identifier")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200577 identifier = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100578 } else if (!strcmp(child->schema->name, "version")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200579 revision = lyd_get_value(child);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200580 if (revision && (revision[0] == '\0')) {
Michal Vasko77367452021-02-16 16:32:18 +0100581 revision = NULL;
Radek Krejci1afa7792017-03-26 11:24:16 -0500582 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100583 } else if (!strcmp(child->schema->name, "format")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200584 format = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100585 }
586 }
Michal Vasko05532772021-06-03 12:12:38 +0200587 VRB(session, "Schema \"%s@%s\" was requested.", identifier, revision ? revision : "<any>");
Michal Vasko05ba9df2016-01-13 14:40:27 +0100588
Michal Vasko77367452021-02-16 16:32:18 +0100589 /* check revision */
590 if (revision && (strlen(revision) != 10) && strcmp(revision, "1.0")) {
591 err = nc_err(server_opts.ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100592 nc_err_set_msg(err, "The requested version is not supported.", "en");
593 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100594 }
595
Michal Vasko77367452021-02-16 16:32:18 +0100596 if (revision) {
597 /* get specific module */
598 module = ly_ctx_get_module(server_opts.ctx, identifier, revision);
599 if (!module) {
600 submodule = ly_ctx_get_submodule(server_opts.ctx, identifier, revision);
601 }
602 } else {
603 /* try to get implemented, then latest module */
604 module = ly_ctx_get_module_implemented(server_opts.ctx, identifier);
605 if (!module) {
606 module = ly_ctx_get_module_latest(server_opts.ctx, identifier);
607 }
608 if (!module) {
609 submodule = ly_ctx_get_submodule_latest(server_opts.ctx, identifier);
610 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200611 }
Michal Vasko77367452021-02-16 16:32:18 +0100612 if (!module && !submodule) {
613 err = nc_err(server_opts.ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100614 nc_err_set_msg(err, "The requested schema was not found.", "en");
615 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100616 }
617
618 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100619 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko77367452021-02-16 16:32:18 +0100620 outformat = LYS_OUT_YANG;
Radek Krejci90fba642016-12-07 15:59:45 +0100621 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko77367452021-02-16 16:32:18 +0100622 outformat = LYS_OUT_YIN;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100623 } else {
Michal Vasko77367452021-02-16 16:32:18 +0100624 err = nc_err(server_opts.ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100625 nc_err_set_msg(err, "The requested format is not supported.", "en");
626 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100627 }
Michal Vasko77367452021-02-16 16:32:18 +0100628
629 /* print */
630 ly_out_new_memory(&model_data, 0, &out);
631 if (module) {
632 lys_print_module(out, module, outformat, 0, 0);
633 } else {
634 lys_print_submodule(out, submodule, outformat, 0, 0);
635 }
636 ly_out_free(out, NULL, 0);
Michal Vaskod91f6e62016-04-05 11:34:22 +0200637 if (!model_data) {
638 ERRINT;
639 return NULL;
640 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100641
Michal Vasko9b1a9522021-03-15 16:24:26 +0100642 /* create reply */
643 mod = ly_ctx_get_module_implemented(server_opts.ctx, "ietf-netconf-monitoring");
644 if (!mod || lyd_new_inner(NULL, mod, "get-schema", 0, &data)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100645 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200646 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100647 return NULL;
648 }
Michal Vasko9b1a9522021-03-15 16:24:26 +0100649 lydict_insert_zc(server_opts.ctx, model_data, (const char **)&model_data);
650 if (lyd_new_any(data, NULL, "data", model_data, 1, LYD_ANYDATA_STRING, 1, NULL)) {
651 ERRINT;
652 lydict_remove(server_opts.ctx, model_data);
653 lyd_free_tree(data);
654 return NULL;
655 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100656
Radek Krejci36dfdb32016-09-01 16:56:35 +0200657 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100658}
659
660static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100661nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100662{
Michal Vasko428087d2016-01-14 16:04:28 +0100663 session->term_reason = NC_SESSION_TERM_CLOSED;
664 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100665}
666
Michal Vasko086311b2016-01-08 09:53:11 +0100667API int
668nc_server_init(struct ly_ctx *ctx)
669{
Michal Vasko77367452021-02-16 16:32:18 +0100670 struct lysc_node *rpc;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000671 pthread_rwlockattr_t attr;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100672
Michal Vasko086311b2016-01-08 09:53:11 +0100673 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200674 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100675 return -1;
676 }
677
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100678 nc_init();
679
Michal Vasko05ba9df2016-01-13 14:40:27 +0100680 /* set default <get-schema> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100681 rpc = NULL;
682 if (ly_ctx_get_module_implemented(ctx, "ietf-netconf-monitoring")) {
683 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
684 }
Michal Vasko88639e92017-08-03 14:38:10 +0200685 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100686 rpc->priv = nc_clb_default_get_schema;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100687 }
688
689 /* set default <close-session> callback if not specififed */
Michal Vasko77367452021-02-16 16:32:18 +0100690 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf:close-session", 0);
Michal Vasko88639e92017-08-03 14:38:10 +0200691 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100692 rpc->priv = nc_clb_default_close_session;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100693 }
694
Michal Vasko086311b2016-01-08 09:53:11 +0100695 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100696
697 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500698 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100699
Michal Vasko77367452021-02-16 16:32:18 +0100700 errno = 0;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000701
702 if (pthread_rwlockattr_init(&attr) == 0) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200703#if defined (HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP)
Frank Rimpler9f838b02018-07-25 06:44:03 +0000704 if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
705 if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200706 ERR(NULL, "%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +0000707 }
708 if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200709 ERR(NULL, "%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +0000710 }
711 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200712 ERR(NULL, "%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +0000713 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700714#endif
Frank Rimpler9f838b02018-07-25 06:44:03 +0000715 pthread_rwlockattr_destroy(&attr);
716 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200717 ERR(NULL, "%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +0000718 }
Michal Vasko086311b2016-01-08 09:53:11 +0100719 return 0;
720}
721
Michal Vaskob48aa812016-01-18 14:13:09 +0100722API void
723nc_server_destroy(void)
724{
Michal Vasko1440a742021-03-31 11:11:03 +0200725 uint32_t i;
Radek Krejci658782b2016-12-04 22:04:55 +0100726
727 for (i = 0; i < server_opts.capabilities_count; i++) {
728 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
729 }
730 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200731 server_opts.capabilities = NULL;
732 server_opts.capabilities_count = 0;
Michal Vasko1440a742021-03-31 11:11:03 +0200733 if (server_opts.content_id_data && server_opts.content_id_data_free) {
734 server_opts.content_id_data_free(server_opts.content_id_data);
735 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200736
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200737#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100738 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200739 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100740#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100741#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100742 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
743 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
744 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200745 server_opts.passwd_auth_data = NULL;
746 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100747
Michal Vasko17dfda92016-12-01 14:06:16 +0100748 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100749
750 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
751 server_opts.hostkey_data_free(server_opts.hostkey_data);
752 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200753 server_opts.hostkey_data = NULL;
754 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100755#endif
756#ifdef NC_ENABLED_TLS
757 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
758 server_opts.server_cert_data_free(server_opts.server_cert_data);
759 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200760 server_opts.server_cert_data = NULL;
761 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100762 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
763 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
764 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200765 server_opts.trusted_cert_list_data = NULL;
766 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100767#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100768 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100769}
770
Michal Vasko086311b2016-01-08 09:53:11 +0100771API int
772nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
773{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200774 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
775 ERRARG("basic_mode");
776 return -1;
777 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
778 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100779 return -1;
780 }
781
782 server_opts.wd_basic_mode = basic_mode;
783 server_opts.wd_also_supported = also_supported;
784 return 0;
785}
786
Michal Vasko1a38c862016-01-15 15:50:07 +0100787API void
Michal Vasko55f03972016-04-13 08:56:01 +0200788nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
789{
790 if (!basic_mode && !also_supported) {
791 ERRARG("basic_mode and also_supported");
792 return;
793 }
794
795 if (basic_mode) {
796 *basic_mode = server_opts.wd_basic_mode;
797 }
798 if (also_supported) {
799 *also_supported = server_opts.wd_also_supported;
800 }
801}
802
Michal Vasko55f03972016-04-13 08:56:01 +0200803API int
Radek Krejci658782b2016-12-04 22:04:55 +0100804nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200805{
Radek Krejci658782b2016-12-04 22:04:55 +0100806 const char **new;
807
808 if (!value || !value[0]) {
809 ERRARG("value must not be empty");
810 return EXIT_FAILURE;
811 }
812
813 server_opts.capabilities_count++;
814 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
815 if (!new) {
816 ERRMEM;
817 return EXIT_FAILURE;
818 }
819 server_opts.capabilities = new;
Michal Vasko77367452021-02-16 16:32:18 +0100820 lydict_insert(server_opts.ctx, value, 0, &server_opts.capabilities[server_opts.capabilities_count - 1]);
Radek Krejci658782b2016-12-04 22:04:55 +0100821
822 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200823}
824
Michal Vasko1a38c862016-01-15 15:50:07 +0100825API void
Michal Vasko1440a742021-03-31 11:11:03 +0200826nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
827 void (*free_user_data)(void *user_data))
828{
829 server_opts.content_id_clb = content_id_clb;
830 server_opts.content_id_data = user_data;
831 server_opts.content_id_data_free = free_user_data;
832}
833
834API void
Michal Vasko086311b2016-01-08 09:53:11 +0100835nc_server_set_hello_timeout(uint16_t hello_timeout)
836{
Michal Vasko086311b2016-01-08 09:53:11 +0100837 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100838}
839
Michal Vasko55f03972016-04-13 08:56:01 +0200840API uint16_t
841nc_server_get_hello_timeout(void)
842{
843 return server_opts.hello_timeout;
844}
845
Michal Vasko1a38c862016-01-15 15:50:07 +0100846API void
Michal Vasko086311b2016-01-08 09:53:11 +0100847nc_server_set_idle_timeout(uint16_t idle_timeout)
848{
Michal Vasko086311b2016-01-08 09:53:11 +0100849 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100850}
851
Michal Vasko55f03972016-04-13 08:56:01 +0200852API uint16_t
853nc_server_get_idle_timeout(void)
854{
855 return server_opts.idle_timeout;
856}
857
Michal Vasko71090fc2016-05-24 16:37:28 +0200858API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100859nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100860{
Michal Vasko71090fc2016-05-24 16:37:28 +0200861 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200862 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200863
Michal Vasko45e53ae2016-04-07 11:46:03 +0200864 if (!server_opts.ctx) {
865 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200866 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200867 } else if (fdin < 0) {
868 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200869 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200870 } else if (fdout < 0) {
871 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200872 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200873 } else if (!username) {
874 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200875 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200876 } else if (!session) {
877 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200878 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100879 }
880
881 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200882 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100883 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100884 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200885 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100886 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100887 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100888
Michal Vasko086311b2016-01-08 09:53:11 +0100889 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100890 (*session)->ti_type = NC_TI_FD;
891 (*session)->ti.fd.in = fdin;
892 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100893
894 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100895 (*session)->flags = NC_SESSION_SHAREDCTX;
896 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100897
Michal Vaskob48aa812016-01-18 14:13:09 +0100898 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200899 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100900
Michal Vasko086311b2016-01-08 09:53:11 +0100901 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200902 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200903 if (msgtype != NC_MSG_HELLO) {
904 nc_session_free(*session, NULL);
905 *session = NULL;
906 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100907 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200908
909 nc_gettimespec_mono(&ts_cur);
910 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
911 nc_gettimespec_real(&ts_cur);
912 (*session)->opts.server.session_start = ts_cur.tv_sec;
913
Michal Vasko1a38c862016-01-15 15:50:07 +0100914 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100915
Michal Vasko71090fc2016-05-24 16:37:28 +0200916 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100917}
Michal Vasko9e036d52016-01-08 10:49:26 +0100918
Michal Vaskob30b99c2016-07-26 11:35:43 +0200919static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100920nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
921{
922 uint8_t q_last;
923
924 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
925 ERRINT;
926 return;
927 }
928
929 /* get a unique queue value (by adding 1 to the last added value, if any) */
930 if (ps->queue_len) {
931 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
932 *id = ps->queue[q_last] + 1;
933 } else {
934 *id = 0;
935 }
936
937 /* add the id into the queue */
938 ++ps->queue_len;
939 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
940 ps->queue[q_last] = *id;
941}
942
943static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200944nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
945{
Michal Vasko74c345f2018-02-07 10:37:11 +0100946 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200947
948 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100949 /* get the actual queue idx */
950 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200951
952 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100953 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200954 /* another equal value, simply cannot be */
955 ERRINT;
956 }
Michal Vaskod8340032018-02-12 14:41:00 +0100957 if (found == 2) {
958 /* move the following values */
959 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
960 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100961 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200962 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100963 if (i == 0) {
964 found = 1;
965 } else {
966 /* this is not okay, our id is in the middle of the queue */
967 found = 2;
968 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200969 }
970 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200971 if (!found) {
972 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100973 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200974 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100975
Michal Vasko103fe632018-02-12 16:37:45 +0100976 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100977 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100978 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100979 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
980 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200981}
982
Michal Vaskof04a52a2016-04-07 10:52:10 +0200983int
Michal Vasko26043172016-07-26 14:08:59 +0200984nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200985{
986 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200987 struct timespec ts;
988
Michal Vasko77a6abe2017-10-05 10:02:20 +0200989 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100990 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200991
992 /* LOCK */
993 ret = pthread_mutex_timedlock(&ps->lock, &ts);
994 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +0200995 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200996 return -1;
997 }
998
Michal Vasko74c345f2018-02-07 10:37:11 +0100999 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +01001000 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko05532772021-06-03 12:12:38 +02001001 ERR(NULL, "%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +01001002 pthread_mutex_unlock(&ps->lock);
1003 return -1;
1004 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001005
1006 /* add ourselves into the queue */
1007 nc_ps_queue_add_id(ps, id);
Michal Vasko05532772021-06-03 12:12:38 +02001008 DBL(NULL, "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 +02001009 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001010
1011 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001012 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001013 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +01001014 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001015
1016 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
1017 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +05301018 /**
1019 * This may happen when another thread releases the lock and broadcasts the condition
1020 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
1021 * but when actually this thread was ready for condition.
1022 */
preetbhansali629dfc42018-12-17 16:04:40 +05301023 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +05301024 break;
1025 }
Michal Vasko66032bc2019-01-22 15:03:12 +01001026
Michal Vasko05532772021-06-03 12:12:38 +02001027 ERR(NULL, "%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001028 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001029 nc_ps_queue_remove_id(ps, *id);
1030 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001031 return -1;
1032 }
1033 }
1034
Michal Vaskobe86fe32016-04-07 10:43:03 +02001035 /* UNLOCK */
1036 pthread_mutex_unlock(&ps->lock);
1037
1038 return 0;
1039}
1040
Michal Vaskof04a52a2016-04-07 10:52:10 +02001041int
Michal Vasko26043172016-07-26 14:08:59 +02001042nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001043{
1044 int ret;
1045 struct timespec ts;
1046
Michal Vasko77a6abe2017-10-05 10:02:20 +02001047 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +01001048 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001049
1050 /* LOCK */
1051 ret = pthread_mutex_timedlock(&ps->lock, &ts);
1052 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001053 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001054 ret = -1;
1055 }
1056
Michal Vaskob30b99c2016-07-26 11:35:43 +02001057 /* we must be the first, it was our turn after all, right? */
1058 if (ps->queue[ps->queue_begin] != id) {
1059 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +02001060 /* UNLOCK */
1061 if (!ret) {
1062 pthread_mutex_unlock(&ps->lock);
1063 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001064 return -1;
1065 }
1066
Michal Vaskobe86fe32016-04-07 10:43:03 +02001067 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001068 nc_ps_queue_remove_id(ps, id);
Michal Vasko05532772021-06-03 12:12:38 +02001069 DBL(NULL, "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 +02001070 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001071
1072 /* broadcast to all other threads that the queue moved */
1073 pthread_cond_broadcast(&ps->cond);
1074
Michal Vaskobe86fe32016-04-07 10:43:03 +02001075 /* UNLOCK */
1076 if (!ret) {
1077 pthread_mutex_unlock(&ps->lock);
1078 }
1079
1080 return ret;
1081}
1082
Michal Vasko428087d2016-01-14 16:04:28 +01001083API struct nc_pollsession *
1084nc_ps_new(void)
1085{
Michal Vasko48a63ed2016-03-01 09:48:21 +01001086 struct nc_pollsession *ps;
1087
1088 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +01001089 if (!ps) {
1090 ERRMEM;
1091 return NULL;
1092 }
Michal Vaskobe86fe32016-04-07 10:43:03 +02001093 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001094 pthread_mutex_init(&ps->lock, NULL);
1095
1096 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +01001097}
1098
1099API void
1100nc_ps_free(struct nc_pollsession *ps)
1101{
fanchanghu3d4e7212017-08-09 09:42:30 +08001102 uint16_t i;
1103
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001104 if (!ps) {
1105 return;
1106 }
1107
Michal Vaskobe86fe32016-04-07 10:43:03 +02001108 if (ps->queue_len) {
Michal Vasko05532772021-06-03 12:12:38 +02001109 ERR(NULL, "FATAL: Freeing a pollsession structure that is currently being worked with!");
Michal Vaskobe86fe32016-04-07 10:43:03 +02001110 }
1111
fanchanghu3d4e7212017-08-09 09:42:30 +08001112 for (i = 0; i < ps->session_count; i++) {
1113 free(ps->sessions[i]);
1114 }
1115
Michal Vasko428087d2016-01-14 16:04:28 +01001116 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001117 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001118 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001119
Michal Vasko428087d2016-01-14 16:04:28 +01001120 free(ps);
1121}
1122
1123API int
1124nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1125{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001126 uint8_t q_id;
1127
Michal Vasko45e53ae2016-04-07 11:46:03 +02001128 if (!ps) {
1129 ERRARG("ps");
1130 return -1;
1131 } else if (!session) {
1132 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001133 return -1;
1134 }
1135
Michal Vasko48a63ed2016-03-01 09:48:21 +01001136 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001137 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001138 return -1;
1139 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001140
Michal Vasko428087d2016-01-14 16:04:28 +01001141 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001142 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001143 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001144 ERRMEM;
1145 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001146 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001147 return -1;
1148 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001149 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1150 if (!ps->sessions[ps->session_count - 1]) {
1151 ERRMEM;
1152 --ps->session_count;
1153 /* UNLOCK */
1154 nc_ps_unlock(ps, q_id, __func__);
1155 return -1;
1156 }
1157 ps->sessions[ps->session_count - 1]->session = session;
1158 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001159
Michal Vasko48a63ed2016-03-01 09:48:21 +01001160 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001161 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001162}
1163
Michal Vasko48a63ed2016-03-01 09:48:21 +01001164static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001165_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001166{
1167 uint16_t i;
1168
Radek Krejcid5f978f2016-03-03 13:14:45 +01001169 if (index >= 0) {
1170 i = (uint16_t)index;
1171 goto remove;
1172 }
Michal Vasko428087d2016-01-14 16:04:28 +01001173 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001174 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001175remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001176 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001177 if (i <= ps->session_count) {
1178 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001179 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001180 }
1181 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001182 free(ps->sessions);
1183 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001184 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001185 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001186 return 0;
1187 }
1188 }
1189
Michal Vaskof0537d82016-01-29 14:42:38 +01001190 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001191}
1192
Michal Vasko48a63ed2016-03-01 09:48:21 +01001193API int
1194nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1195{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001196 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001197 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001198
Michal Vasko45e53ae2016-04-07 11:46:03 +02001199 if (!ps) {
1200 ERRARG("ps");
1201 return -1;
1202 } else if (!session) {
1203 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001204 return -1;
1205 }
1206
1207 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001208 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001209 return -1;
1210 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001211
Radek Krejcid5f978f2016-03-03 13:14:45 +01001212 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001213
1214 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001215 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001216
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001217 return ret || ret2 ? -1 : 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001218}
1219
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001220API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001221nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001222{
1223 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001224 struct nc_session *ret = NULL;
1225
1226 if (!ps) {
1227 ERRARG("ps");
1228 return NULL;
1229 }
1230
1231 /* LOCK */
1232 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1233 return NULL;
1234 }
1235
Michal Vasko4871c9d2017-10-09 14:48:39 +02001236 if (idx < ps->session_count) {
1237 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001238 }
1239
1240 /* UNLOCK */
1241 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1242
1243 return ret;
1244}
1245
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001246API uint16_t
1247nc_ps_session_count(struct nc_pollsession *ps)
1248{
Michal Vasko47003942019-03-14 12:25:23 +01001249 uint8_t q_id;
1250 uint16_t session_count;
1251
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001252 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001253 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001254 return 0;
1255 }
1256
Michal Vasko47003942019-03-14 12:25:23 +01001257 /* LOCK (just for memory barrier so that we read the current value) */
1258 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1259 return 0;
1260 }
1261
1262 session_count = ps->session_count;
1263
1264 /* UNLOCK */
1265 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1266
1267 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001268}
1269
Michal Vasko131120a2018-05-29 15:44:02 +02001270/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001271 * returns: NC_PSPOLL_ERROR,
Michal Vasko77367452021-02-16 16:32:18 +01001272 * NC_PSPOLL_TIMEOUT,
Michal Vasko71090fc2016-05-24 16:37:28 +02001273 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1274 * NC_PSPOLL_RPC
1275 */
1276static int
Michal Vasko131120a2018-05-29 15:44:02 +02001277nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001278{
Michal Vasko77367452021-02-16 16:32:18 +01001279 struct ly_in *msg;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001280 struct nc_server_reply *reply = NULL;
Michal Vasko939ffce2021-04-12 13:02:01 +02001281 struct lyd_node *e;
Michal Vasko77367452021-02-16 16:32:18 +01001282 int r, ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001283
Michal Vasko45e53ae2016-04-07 11:46:03 +02001284 if (!session) {
1285 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001286 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001287 } else if (!rpc) {
1288 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001289 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001290 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vasko05532772021-06-03 12:12:38 +02001291 ERR(session, "Invalid session to receive RPCs.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001292 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001293 }
1294
Michal Vasko77367452021-02-16 16:32:18 +01001295 /* get a message */
1296 r = nc_read_msg_io(session, io_timeout, &msg, 0);
1297 if (r == -2) {
1298 /* malformed message */
1299 ret = NC_PSPOLL_REPLY_ERROR;
1300 reply = nc_server_reply_err(nc_err(server_opts.ctx, NC_ERR_MALFORMED_MSG));
1301 goto send_reply;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001302 }
1303 if (r == -1) {
Michal Vasko77367452021-02-16 16:32:18 +01001304 return NC_PSPOLL_ERROR;
1305 } else if (!r) {
1306 return NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001307 }
1308
Michal Vasko77367452021-02-16 16:32:18 +01001309 *rpc = calloc(1, sizeof **rpc);
1310 if (!*rpc) {
1311 ERRMEM;
1312 ret = NC_PSPOLL_REPLY_ERROR;
1313 goto cleanup;
1314 }
1315
1316 /* parse the RPC */
1317 if (lyd_parse_op(server_opts.ctx, NULL, msg, LYD_XML, LYD_TYPE_RPC_NETCONF, &(*rpc)->envp, &(*rpc)->rpc)) {
1318 /* bad RPC received */
1319 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1320
1321 if ((*rpc)->envp) {
1322 /* at least the envelopes were parsed */
Michal Vasko939ffce2021-04-12 13:02:01 +02001323 e = nc_err(server_opts.ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
1324 nc_err_set_msg(e, ly_errmsg(server_opts.ctx), "en");
1325 reply = nc_server_reply_err(e);
Michal Vasko77367452021-02-16 16:32:18 +01001326 } else if (session->version == NC_VERSION_11) {
1327 /* completely malformed message, NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
1328 reply = nc_server_reply_err(nc_err(server_opts.ctx, NC_ERR_MALFORMED_MSG));
1329 }
1330
1331send_reply:
1332 if (reply) {
1333 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, (*rpc)->envp, reply);
1334 nc_server_reply_free(reply);
1335 if (r != NC_MSG_REPLY) {
Michal Vasko05532772021-06-03 12:12:38 +02001336 ERR(session, "Failed to write reply (%s), terminating session.", nc_msgtype2str[r]);
Michal Vasko77367452021-02-16 16:32:18 +01001337 if (session->status != NC_STATUS_INVALID) {
1338 session->status = NC_STATUS_INVALID;
1339 session->term_reason = NC_SESSION_TERM_OTHER;
1340 }
1341 }
1342 }
1343 } else {
1344 ret = NC_PSPOLL_RPC;
1345 }
1346
1347cleanup:
1348 ly_in_free(msg, 1);
1349 if (ret != NC_PSPOLL_RPC) {
1350 nc_server_rpc_free(*rpc);
1351 *rpc = NULL;
1352 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001353 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001354}
1355
fanchanghu966f2de2016-07-21 02:28:57 -04001356API void
1357nc_set_global_rpc_clb(nc_rpc_clb clb)
1358{
1359 global_rpc_clb = clb;
1360}
1361
Radek Krejci93e80222016-10-03 13:34:25 +02001362API NC_MSG_TYPE
1363nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1364{
Michal Vasko131120a2018-05-29 15:44:02 +02001365 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001366
1367 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001368 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001369 ERRARG("session");
1370 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001371 } else if (!notif || !notif->ntf || !notif->eventtime) {
Radek Krejci93e80222016-10-03 13:34:25 +02001372 ERRARG("notif");
1373 return NC_MSG_ERROR;
1374 }
1375
Michal Vasko131120a2018-05-29 15:44:02 +02001376 /* we do not need RPC lock for this, IO lock will be acquired properly */
1377 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001378 if (ret != NC_MSG_NOTIF) {
Michal Vasko05532772021-06-03 12:12:38 +02001379 ERR(session, "Failed to write notification (%s).", nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001380 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001381
Michal Vasko131120a2018-05-29 15:44:02 +02001382 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001383}
1384
Michal Vasko131120a2018-05-29 15:44:02 +02001385/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001386 * returns: NC_PSPOLL_ERROR,
1387 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1388 * NC_PSPOLL_REPLY_ERROR,
1389 * 0
1390 */
1391static int
Michal Vasko131120a2018-05-29 15:44:02 +02001392nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001393{
1394 nc_rpc_clb clb;
1395 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +01001396 const struct lysc_node *rpc_act = NULL;
1397 struct lyd_node *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001398 int ret = 0;
1399 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001400
Michal Vasko4a827e52016-03-03 10:59:00 +01001401 if (!rpc) {
1402 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001403 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001404 }
1405
Michal Vasko77367452021-02-16 16:32:18 +01001406 if (rpc->rpc->schema->nodetype == LYS_RPC) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001407 /* RPC */
Michal Vasko77367452021-02-16 16:32:18 +01001408 rpc_act = rpc->rpc->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001409 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001410 /* action */
Michal Vasko77367452021-02-16 16:32:18 +01001411 LYD_TREE_DFS_BEGIN(rpc->rpc, elem) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001412 if (elem->schema->nodetype == LYS_ACTION) {
1413 rpc_act = elem->schema;
1414 break;
1415 }
Michal Vasko77367452021-02-16 16:32:18 +01001416 LYD_TREE_DFS_END(rpc->rpc, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001417 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001418 if (!rpc_act) {
1419 ERRINT;
1420 return NC_PSPOLL_ERROR;
1421 }
1422 }
1423
1424 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001425 if (!global_rpc_clb) {
1426 /* no callback, reply with a not-implemented error */
Michal Vasko77367452021-02-16 16:32:18 +01001427 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 +03001428 } else {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001429 reply = global_rpc_clb(rpc->rpc, session);
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001430 }
Michal Vasko428087d2016-01-14 16:04:28 +01001431 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001432 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko77367452021-02-16 16:32:18 +01001433 reply = clb(rpc->rpc, session);
Michal Vasko428087d2016-01-14 16:04:28 +01001434 }
1435
1436 if (!reply) {
Michal Vasko77367452021-02-16 16:32:18 +01001437 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 +01001438 }
Michal Vasko77367452021-02-16 16:32:18 +01001439 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->envp, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001440 if (reply->type == NC_RPL_ERROR) {
1441 ret |= NC_PSPOLL_REPLY_ERROR;
1442 }
1443 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001444
Michal Vasko131120a2018-05-29 15:44:02 +02001445 if (r != NC_MSG_REPLY) {
Michal Vasko15469492021-06-09 08:40:48 +02001446 ERR(session, "Failed to write reply (%s).", nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001447 ret |= NC_PSPOLL_ERROR;
1448 }
Michal Vasko428087d2016-01-14 16:04:28 +01001449
1450 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1451 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1452 session->status = NC_STATUS_INVALID;
1453 }
1454
Michal Vasko71090fc2016-05-24 16:37:28 +02001455 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001456}
1457
Michal Vasko131120a2018-05-29 15:44:02 +02001458/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001459 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1460 * NC_PSPOLL_ERROR, (msg filled)
1461 * NC_PSPOLL_TIMEOUT,
1462 * NC_PSPOLL_RPC (some application data available),
1463 * NC_PSPOLL_SSH_CHANNEL,
1464 * NC_PSPOLL_SSH_MSG
1465 */
1466static int
Michal Vasko131120a2018-05-29 15:44:02 +02001467nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001468{
Michal Vasko9a327362017-01-11 11:31:46 +01001469 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001470 int r, ret = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001471
Michal Vasko9a327362017-01-11 11:31:46 +01001472#ifdef NC_ENABLED_SSH
1473 struct nc_session *new;
1474#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001475
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001476 /* check timeout first */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001477 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout &&
1478 (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001479 sprintf(msg, "session idle timeout elapsed");
1480 session->status = NC_STATUS_INVALID;
1481 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1482 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1483 }
1484
Michal Vasko131120a2018-05-29 15:44:02 +02001485 r = nc_session_io_lock(session, io_timeout, __func__);
1486 if (r < 0) {
1487 sprintf(msg, "session IO lock failed to be acquired");
1488 return NC_PSPOLL_ERROR;
1489 } else if (!r) {
1490 return NC_PSPOLL_TIMEOUT;
1491 }
1492
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001493 switch (session->ti_type) {
1494#ifdef NC_ENABLED_SSH
1495 case NC_TI_LIBSSH:
1496 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001497 if (r == SSH_EOF) {
1498 sprintf(msg, "SSH channel unexpected EOF");
1499 session->status = NC_STATUS_INVALID;
1500 session->term_reason = NC_SESSION_TERM_DROPPED;
1501 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1502 } else if (r == SSH_ERROR) {
1503 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001504 session->status = NC_STATUS_INVALID;
1505 session->term_reason = NC_SESSION_TERM_OTHER;
1506 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001507 } else if (!r) {
1508 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1509 /* new SSH message */
1510 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1511 if (session->ti.libssh.next) {
1512 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001513 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel &&
1514 (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko8dcaa882017-10-19 14:28:42 +02001515 /* new NETCONF SSH channel */
1516 ret = NC_PSPOLL_SSH_CHANNEL;
1517 break;
1518 }
1519 }
1520 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001521 break;
1522 }
1523 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001524
Michal Vasko8dcaa882017-10-19 14:28:42 +02001525 /* just some SSH message */
1526 ret = NC_PSPOLL_SSH_MSG;
1527 } else {
1528 ret = NC_PSPOLL_TIMEOUT;
1529 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001530 } else {
1531 /* we have some application data */
1532 ret = NC_PSPOLL_RPC;
1533 }
1534 break;
1535#endif
1536#ifdef NC_ENABLED_TLS
1537 case NC_TI_OPENSSL:
1538 r = SSL_pending(session->ti.tls);
1539 if (!r) {
1540 /* no data pending in the SSL buffer, poll fd */
1541 pfd.fd = SSL_get_rfd(session->ti.tls);
1542 if (pfd.fd < 0) {
1543 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1544 ret = NC_PSPOLL_ERROR;
1545 break;
1546 }
1547 pfd.events = POLLIN;
1548 pfd.revents = 0;
1549 r = poll(&pfd, 1, 0);
1550
1551 if ((r < 0) && (errno != EINTR)) {
1552 sprintf(msg, "poll failed (%s)", strerror(errno));
1553 session->status = NC_STATUS_INVALID;
1554 ret = NC_PSPOLL_ERROR;
1555 } else if (r > 0) {
1556 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1557 sprintf(msg, "communication socket unexpectedly closed");
1558 session->status = NC_STATUS_INVALID;
1559 session->term_reason = NC_SESSION_TERM_DROPPED;
1560 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1561 } else if (pfd.revents & POLLERR) {
1562 sprintf(msg, "communication socket error");
1563 session->status = NC_STATUS_INVALID;
1564 session->term_reason = NC_SESSION_TERM_OTHER;
1565 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1566 } else {
1567 ret = NC_PSPOLL_RPC;
1568 }
1569 } else {
1570 ret = NC_PSPOLL_TIMEOUT;
1571 }
1572 } else {
1573 ret = NC_PSPOLL_RPC;
1574 }
1575 break;
1576#endif
1577 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001578 case NC_TI_UNIX:
1579 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001580 pfd.events = POLLIN;
1581 pfd.revents = 0;
1582 r = poll(&pfd, 1, 0);
1583
1584 if ((r < 0) && (errno != EINTR)) {
1585 sprintf(msg, "poll failed (%s)", strerror(errno));
1586 session->status = NC_STATUS_INVALID;
1587 ret = NC_PSPOLL_ERROR;
1588 } else if (r > 0) {
1589 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1590 sprintf(msg, "communication socket unexpectedly closed");
1591 session->status = NC_STATUS_INVALID;
1592 session->term_reason = NC_SESSION_TERM_DROPPED;
1593 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1594 } else if (pfd.revents & POLLERR) {
1595 sprintf(msg, "communication socket error");
1596 session->status = NC_STATUS_INVALID;
1597 session->term_reason = NC_SESSION_TERM_OTHER;
1598 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1599 } else {
1600 ret = NC_PSPOLL_RPC;
1601 }
1602 } else {
1603 ret = NC_PSPOLL_TIMEOUT;
1604 }
1605 break;
1606 case NC_TI_NONE:
1607 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1608 ret = NC_PSPOLL_ERROR;
1609 break;
1610 }
1611
Michal Vasko131120a2018-05-29 15:44:02 +02001612 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001613 return ret;
1614}
1615
1616API int
1617nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1618{
1619 int ret, r;
1620 uint8_t q_id;
1621 uint16_t i, j;
1622 char msg[256];
1623 struct timespec ts_timeout, ts_cur;
1624 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001625 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001626 struct nc_server_rpc *rpc = NULL;
1627
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001628 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001629 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001630 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001631 }
1632
Michal Vaskoade892d2017-02-22 13:40:35 +01001633 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001634 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001635 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001636 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001637
Michal Vaskoade892d2017-02-22 13:40:35 +01001638 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001639 nc_ps_unlock(ps, q_id, __func__);
1640 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001641 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001642
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001643 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001644 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001645 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001646 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001647 nc_addtimespec(&ts_timeout, timeout);
1648 }
1649
1650 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001651 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001652 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001653 if (ps->last_event_session == ps->session_count - 1) {
1654 i = j = 0;
1655 } else {
1656 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001657 }
Michal Vasko9a327362017-01-11 11:31:46 +01001658 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001659 cur_ps_session = ps->sessions[i];
1660 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001661
Michal Vasko131120a2018-05-29 15:44:02 +02001662 /* SESSION RPC LOCK */
1663 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001664 if (r == -1) {
1665 ret = NC_PSPOLL_ERROR;
1666 } else if (r == 1) {
1667 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001668 switch (cur_ps_session->state) {
1669 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001670 if (cur_session->status == NC_STATUS_RUNNING) {
1671 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001672 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001673
Michal Vasko131120a2018-05-29 15:44:02 +02001674 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001675 switch (ret) {
1676 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001677 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001678 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001679 break;
1680 case NC_PSPOLL_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001681 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001682 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001683 break;
1684 case NC_PSPOLL_TIMEOUT:
1685#ifdef NC_ENABLED_SSH
1686 case NC_PSPOLL_SSH_CHANNEL:
1687 case NC_PSPOLL_SSH_MSG:
1688#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001689 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001690 break;
1691 case NC_PSPOLL_RPC:
1692 /* let's keep the state busy, we are not done with this session */
1693 break;
1694 }
1695 } else {
1696 /* session is not fine, let the caller know */
1697 ret = NC_PSPOLL_SESSION_TERM;
1698 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1699 ret |= NC_PSPOLL_SESSION_ERROR;
1700 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001701 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001702 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001703 break;
1704 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001705 /* it definitely should not be busy because we have the lock */
1706 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001707 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001708 break;
1709 case NC_PS_STATE_INVALID:
1710 /* we got it locked, but it will be freed, let it be */
1711 ret = NC_PSPOLL_TIMEOUT;
1712 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001713 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001714
Michal Vasko131120a2018-05-29 15:44:02 +02001715 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001716 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001717 /* SESSION RPC UNLOCK */
1718 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001719 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001720 } else {
1721 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001722 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001723 }
Michal Vasko428087d2016-01-14 16:04:28 +01001724
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001725 /* something happened */
1726 if (ret != NC_PSPOLL_TIMEOUT) {
1727 break;
1728 }
1729
Michal Vasko9a327362017-01-11 11:31:46 +01001730 if (i == ps->session_count - 1) {
1731 i = 0;
1732 } else {
1733 ++i;
1734 }
1735 } while (i != j);
1736
Michal Vaskoade892d2017-02-22 13:40:35 +01001737 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001738 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001739 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001740 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001741 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001742
1743 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1744 /* final timeout */
1745 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001746 }
Michal Vasko428087d2016-01-14 16:04:28 +01001747 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001748 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001749
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001750 /* do we want to return the session? */
1751 switch (ret) {
1752 case NC_PSPOLL_RPC:
1753 case NC_PSPOLL_SESSION_TERM:
1754 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1755#ifdef NC_ENABLED_SSH
1756 case NC_PSPOLL_SSH_CHANNEL:
1757 case NC_PSPOLL_SSH_MSG:
1758#endif
1759 if (session) {
1760 *session = cur_session;
1761 }
1762 ps->last_event_session = i;
1763 break;
1764 default:
1765 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001766 }
Michal Vasko428087d2016-01-14 16:04:28 +01001767
Michal Vaskoade892d2017-02-22 13:40:35 +01001768 /* PS UNLOCK */
1769 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001770
Michal Vasko131120a2018-05-29 15:44:02 +02001771 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001772 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001773 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001774 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1775 if (cur_session->status != NC_STATUS_RUNNING) {
1776 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001777 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001778 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001779 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001780 }
1781 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001782 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001783
Michal Vasko7f1ee932018-10-11 09:41:42 +02001784 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001785 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001786 if (cur_session->status != NC_STATUS_RUNNING) {
1787 ret |= NC_PSPOLL_SESSION_TERM;
1788 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1789 ret |= NC_PSPOLL_SESSION_ERROR;
1790 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001791 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001792 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001793 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001794 }
Michal Vasko428087d2016-01-14 16:04:28 +01001795 }
Michal Vasko77367452021-02-16 16:32:18 +01001796 nc_server_rpc_free(rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001797
Michal Vasko131120a2018-05-29 15:44:02 +02001798 /* SESSION RPC UNLOCK */
1799 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001800 }
1801
Michal Vasko48a63ed2016-03-01 09:48:21 +01001802 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001803}
1804
Michal Vaskod09eae62016-02-01 10:32:52 +01001805API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001806nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001807{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001808 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001809 uint16_t i;
1810 struct nc_session *session;
1811
Michal Vasko9a25e932016-02-01 10:36:42 +01001812 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001813 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001814 return;
1815 }
1816
Michal Vasko48a63ed2016-03-01 09:48:21 +01001817 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001818 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001819 return;
1820 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001821
Michal Vasko48a63ed2016-03-01 09:48:21 +01001822 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001823 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001824 nc_session_free(ps->sessions[i]->session, data_free);
1825 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001826 }
1827 free(ps->sessions);
1828 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001829 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001830 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001831 } else {
1832 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001833 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1834 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001835 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001836 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001837 continue;
1838 }
1839
1840 ++i;
1841 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001842 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001843
1844 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001845 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001846}
1847
Michal Vasko5f352c52019-07-10 16:12:06 +02001848static int
apropp-molex4e903c32020-04-20 03:06:58 -04001849nc_get_uid(int sock, uid_t *uid)
1850{
Michal Vaskod3910912020-04-20 09:12:49 +02001851 int ret;
apropp-molex4e903c32020-04-20 03:06:58 -04001852
Michal Vaskod3910912020-04-20 09:12:49 +02001853#ifdef SO_PEERCRED
1854 struct ucred ucred;
1855 socklen_t len;
1856 len = sizeof(ucred);
1857 ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
1858 if (!ret) {
1859 *uid = ucred.uid;
1860 }
1861#else
1862 ret = getpeereid(sock, uid, NULL);
1863#endif
1864
1865 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001866 ERR(NULL, "Failed to get credentials from unix socket (%s).", strerror(errno));
Michal Vaskod3910912020-04-20 09:12:49 +02001867 return -1;
1868 }
apropp-molex4e903c32020-04-20 03:06:58 -04001869 return 0;
1870}
1871
1872static int
Michal Vasko5f352c52019-07-10 16:12:06 +02001873nc_accept_unix(struct nc_session *session, int sock)
1874{
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001875#if defined (SO_PEERCRED) || defined (HAVE_GETPEEREID)
Michal Vasko5f352c52019-07-10 16:12:06 +02001876 const struct passwd *pw;
Michal Vasko5f352c52019-07-10 16:12:06 +02001877 char *username;
Michal Vasko5f352c52019-07-10 16:12:06 +02001878 session->ti_type = NC_TI_UNIX;
apropp-molex4e903c32020-04-20 03:06:58 -04001879 uid_t uid;
Michal Vasko5f352c52019-07-10 16:12:06 +02001880
Michal Vaskod3910912020-04-20 09:12:49 +02001881 if (nc_get_uid(sock, &uid)) {
1882 close(sock);
Michal Vasko5f352c52019-07-10 16:12:06 +02001883 return -1;
1884 }
1885
apropp-molex4e903c32020-04-20 03:06:58 -04001886 pw = getpwuid(uid);
Michal Vasko5f352c52019-07-10 16:12:06 +02001887 if (pw == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001888 ERR(NULL, "Failed to find username for uid=%u (%s).\n", uid, strerror(errno));
Michal Vasko5f352c52019-07-10 16:12:06 +02001889 close(sock);
1890 return -1;
1891 }
1892
1893 username = strdup(pw->pw_name);
1894 if (username == NULL) {
1895 ERRMEM;
1896 close(sock);
1897 return -1;
1898 }
Michal Vasko77367452021-02-16 16:32:18 +01001899 lydict_insert_zc(server_opts.ctx, username, &session->username);
Michal Vasko5f352c52019-07-10 16:12:06 +02001900
1901 session->ti.unixsock.sock = sock;
1902
1903 return 1;
Claus Klein22091912020-01-20 13:45:47 +01001904#else
1905 return -1;
1906#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02001907}
1908
Michal Vaskoe2713da2016-08-22 16:06:40 +02001909API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001910nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001911{
Michal Vasko3031aae2016-01-27 16:07:18 +01001912 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001913 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001914
Michal Vasko45e53ae2016-04-07 11:46:03 +02001915 if (!name) {
1916 ERRARG("name");
1917 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001918 }
1919
Michal Vaskoade892d2017-02-22 13:40:35 +01001920 /* BIND LOCK */
1921 pthread_mutex_lock(&server_opts.bind_lock);
1922
1923 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001924 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001925
1926 /* check name uniqueness */
1927 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001928 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02001929 ERR(NULL, "Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001930 ret = -1;
1931 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001932 }
1933 }
1934
Michal Vasko3031aae2016-01-27 16:07:18 +01001935 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001936 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001937 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001938 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001939 ret = -1;
1940 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001941 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001942 memset(&server_opts.endpts[server_opts.endpt_count - 1], 0, sizeof *server_opts.endpts);
Michal Vasko77367452021-02-16 16:32:18 +01001943 lydict_insert(server_opts.ctx, name, 0, &server_opts.endpts[server_opts.endpt_count - 1].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001944 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001945 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
1946 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
1947 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001948
Michal Vaskoe2713da2016-08-22 16:06:40 +02001949 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001950 if (!server_opts.binds) {
1951 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001952 ret = -1;
1953 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001954 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001955
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001956 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001957 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1958
1959 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001960#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001961 case NC_TI_LIBSSH:
1962 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1963 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1964 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001965 ret = -1;
1966 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001967 }
1968 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
Michal Vasko77367452021-02-16 16:32:18 +01001969 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001970 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02001971 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001972 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001973#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001974#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001975 case NC_TI_OPENSSL:
1976 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1977 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1978 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001979 ret = -1;
1980 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001981 }
1982 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001983#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001984 case NC_TI_UNIX:
1985 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1986 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1987 ERRMEM;
1988 ret = -1;
1989 goto cleanup;
1990 }
1991 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1992 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1993 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1994 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001995 default:
1996 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001997 ret = -1;
1998 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001999 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002000
Michal Vaskoade892d2017-02-22 13:40:35 +01002001cleanup:
2002 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002003 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002004
Michal Vaskoade892d2017-02-22 13:40:35 +01002005 /* BIND UNLOCK */
2006 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002007
Michal Vaskoade892d2017-02-22 13:40:35 +01002008 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002009}
2010
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002011API int
Michal Vasko59050372016-11-22 14:33:55 +01002012nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01002013{
2014 uint32_t i;
2015 int ret = -1;
2016
Michal Vaskoade892d2017-02-22 13:40:35 +01002017 /* BIND LOCK */
2018 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002019
Michal Vaskoade892d2017-02-22 13:40:35 +01002020 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002021 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002022
Michal Vasko59050372016-11-22 14:33:55 +01002023 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002024 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01002025 for (i = 0; i < server_opts.endpt_count; ++i) {
2026 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002027 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002028#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002029 case NC_TI_LIBSSH:
2030 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2031 free(server_opts.endpts[i].opts.ssh);
2032 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002033#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002034#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002035 case NC_TI_OPENSSL:
2036 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2037 free(server_opts.endpts[i].opts.tls);
2038 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002039#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002040 case NC_TI_UNIX:
2041 free(server_opts.endpts[i].opts.unixsock);
2042 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002043 default:
2044 ERRINT;
2045 /* won't get here ...*/
2046 break;
2047 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002048 ret = 0;
2049 }
Michal Vasko3031aae2016-01-27 16:07:18 +01002050 free(server_opts.endpts);
2051 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002052
2053 /* remove all binds */
2054 for (i = 0; i < server_opts.endpt_count; ++i) {
2055 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2056 if (server_opts.binds[i].sock > -1) {
2057 close(server_opts.binds[i].sock);
2058 }
2059 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002060 free(server_opts.binds);
2061 server_opts.binds = NULL;
2062
Michal Vasko3031aae2016-01-27 16:07:18 +01002063 server_opts.endpt_count = 0;
2064
Michal Vasko1a38c862016-01-15 15:50:07 +01002065 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002066 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002067 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002068 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002069 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01002070 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002071 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002072#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002073 case NC_TI_LIBSSH:
2074 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2075 free(server_opts.endpts[i].opts.ssh);
2076 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002077#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002078#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002079 case NC_TI_OPENSSL:
2080 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2081 free(server_opts.endpts[i].opts.tls);
2082 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002083#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002084 case NC_TI_UNIX:
2085 free(server_opts.endpts[i].opts.unixsock);
2086 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002087 default:
2088 ERRINT;
2089 break;
2090 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002091
Michal Vaskoe2713da2016-08-22 16:06:40 +02002092 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02002093 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2094 if (server_opts.binds[i].sock > -1) {
2095 close(server_opts.binds[i].sock);
2096 }
2097
2098 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002099 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002100 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002101 free(server_opts.binds);
2102 server_opts.binds = NULL;
2103 free(server_opts.endpts);
2104 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002105 } else if (i < server_opts.endpt_count) {
2106 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2107 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002108 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002109
2110 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002111 if (name) {
2112 break;
2113 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002114 }
2115 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002116 }
2117
Michal Vaskoade892d2017-02-22 13:40:35 +01002118 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002119 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002120
Michal Vaskoade892d2017-02-22 13:40:35 +01002121 /* BIND UNLOCK */
2122 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002123
2124 return ret;
2125}
2126
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002127API int
2128nc_server_endpt_count(void)
2129{
2130 return server_opts.endpt_count;
2131}
2132
Michal Vasko1b5973e2020-01-30 16:05:46 +01002133API int
2134nc_server_is_endpt(const char *name)
2135{
2136 uint16_t i;
2137 int found = 0;
2138
Michal Vaskofb1724b2020-01-31 11:02:00 +01002139 if (!name) {
2140 return found;
2141 }
2142
Michal Vasko1b5973e2020-01-30 16:05:46 +01002143 /* ENDPT READ LOCK */
2144 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2145
2146 /* check name uniqueness */
2147 for (i = 0; i < server_opts.endpt_count; ++i) {
2148 if (!strcmp(server_opts.endpts[i].name, name)) {
2149 found = 1;
2150 break;
2151 }
2152 }
2153
2154 /* ENDPT UNLOCK */
2155 pthread_rwlock_unlock(&server_opts.endpt_lock);
2156
2157 return found;
2158}
2159
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002160int
2161nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
2162{
2163 struct nc_endpt *endpt;
2164 struct nc_bind *bind = NULL;
2165 uint16_t i;
2166 int sock = -1, set_addr, ret = 0;
2167
2168 if (!endpt_name) {
2169 ERRARG("endpt_name");
2170 return -1;
2171 } else if ((!address && !port) || (address && port)) {
2172 ERRARG("address and port");
2173 return -1;
2174 }
2175
2176 if (address) {
2177 set_addr = 1;
2178 } else {
2179 set_addr = 0;
2180 }
2181
2182 /* BIND LOCK */
2183 pthread_mutex_lock(&server_opts.bind_lock);
2184
2185 /* ENDPT LOCK */
2186 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2187 if (!endpt) {
2188 /* BIND UNLOCK */
2189 pthread_mutex_unlock(&server_opts.bind_lock);
2190 return -1;
2191 }
2192
2193 bind = &server_opts.binds[i];
2194
2195 if (set_addr) {
2196 port = bind->port;
2197 } else {
2198 address = bind->address;
2199 }
2200
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002201 if (!set_addr && (endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002202 ret = -1;
2203 goto cleanup;
2204 }
2205
2206 /* we have all the information we need to create a listening socket */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002207 if (address && (port || (endpt->ti == NC_TI_UNIX))) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002208 /* create new socket, close the old one */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002209 if (endpt->ti == NC_TI_UNIX) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002210 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002211 } else {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002212 sock = nc_sock_listen_inet(address, port, &endpt->ka);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002213 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002214 if (sock == -1) {
2215 ret = -1;
2216 goto cleanup;
2217 }
2218
2219 if (bind->sock > -1) {
2220 close(bind->sock);
2221 }
2222 bind->sock = sock;
2223 } /* else we are just setting address or port */
2224
2225 if (set_addr) {
2226 lydict_remove(server_opts.ctx, bind->address);
Michal Vasko77367452021-02-16 16:32:18 +01002227 lydict_insert(server_opts.ctx, address, 0, &bind->address);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002228 } else {
2229 bind->port = port;
2230 }
2231
2232 if (sock > -1) {
Michal Vasko946cacb2020-08-12 11:18:08 +02002233 switch (endpt->ti) {
2234 case NC_TI_UNIX:
Michal Vasko05532772021-06-03 12:12:38 +02002235 VRB(NULL, "Listening on %s for UNIX connections.", address);
Michal Vasko946cacb2020-08-12 11:18:08 +02002236 break;
2237#ifdef NC_ENABLED_SSH
2238 case NC_TI_LIBSSH:
Michal Vasko05532772021-06-03 12:12:38 +02002239 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002240 break;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002241#endif
Michal Vasko946cacb2020-08-12 11:18:08 +02002242#ifdef NC_ENABLED_TLS
2243 case NC_TI_OPENSSL:
Michal Vasko05532772021-06-03 12:12:38 +02002244 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002245 break;
2246#endif
2247 default:
2248 ERRINT;
2249 break;
2250 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002251 }
2252
2253cleanup:
2254 /* ENDPT UNLOCK */
2255 pthread_rwlock_unlock(&server_opts.endpt_lock);
2256
2257 /* BIND UNLOCK */
2258 pthread_mutex_unlock(&server_opts.bind_lock);
2259
2260 return ret;
2261}
2262
2263API int
2264nc_server_endpt_set_address(const char *endpt_name, const char *address)
2265{
2266 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2267}
2268
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002269#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002270
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002271API int
2272nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2273{
2274 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2275}
2276
Michal Vasko946cacb2020-08-12 11:18:08 +02002277#endif
2278
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002279API int
2280nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2281{
2282 struct nc_endpt *endpt;
2283 uint16_t i;
2284 int ret = 0;
2285
2286 if (!endpt_name) {
2287 ERRARG("endpt_name");
2288 return -1;
2289 } else if (mode == 0) {
2290 ERRARG("mode");
2291 return -1;
2292 }
2293
2294 /* ENDPT LOCK */
2295 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002296 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002297 return -1;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002298 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002299
2300 if (endpt->ti != NC_TI_UNIX) {
2301 ret = -1;
2302 goto cleanup;
2303 }
2304
2305 endpt->opts.unixsock->mode = mode;
2306 endpt->opts.unixsock->uid = uid;
2307 endpt->opts.unixsock->gid = gid;
2308
2309cleanup:
2310 /* ENDPT UNLOCK */
2311 pthread_rwlock_unlock(&server_opts.endpt_lock);
2312
2313 return ret;
2314}
2315
2316API int
2317nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2318{
2319 struct nc_endpt *endpt;
2320 int ret = 0;
2321
2322 if (!endpt_name) {
2323 ERRARG("endpt_name");
2324 return -1;
2325 }
2326
2327 /* ENDPT LOCK */
2328 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2329 if (!endpt) {
2330 return -1;
2331 }
2332
2333 endpt->ka.enabled = (enable ? 1 : 0);
2334
2335 /* ENDPT UNLOCK */
2336 pthread_rwlock_unlock(&server_opts.endpt_lock);
2337
2338 return ret;
2339}
2340
2341API int
2342nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2343{
2344 struct nc_endpt *endpt;
2345 int ret = 0;
2346
2347 if (!endpt_name) {
2348 ERRARG("endpt_name");
2349 return -1;
2350 }
2351
2352 /* ENDPT LOCK */
2353 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2354 if (!endpt) {
2355 return -1;
2356 }
2357
2358 if (idle_time > -1) {
2359 endpt->ka.idle_time = idle_time;
2360 }
2361 if (max_probes > -1) {
2362 endpt->ka.max_probes = max_probes;
2363 }
2364 if (probe_interval > -1) {
2365 endpt->ka.probe_interval = probe_interval;
2366 }
2367
2368 /* ENDPT UNLOCK */
2369 pthread_rwlock_unlock(&server_opts.endpt_lock);
2370
2371 return ret;
2372}
2373
Michal Vasko71090fc2016-05-24 16:37:28 +02002374API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002375nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002376{
Michal Vasko71090fc2016-05-24 16:37:28 +02002377 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002378 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002379 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002380 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002381 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002382
Michal Vasko45e53ae2016-04-07 11:46:03 +02002383 if (!server_opts.ctx) {
2384 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002385 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002386 } else if (!session) {
2387 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002388 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002389 }
2390
Michal Vaskoade892d2017-02-22 13:40:35 +01002391 /* BIND LOCK */
2392 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002393
2394 if (!server_opts.endpt_count) {
Michal Vasko05532772021-06-03 12:12:38 +02002395 ERR(NULL, "No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002396 /* BIND UNLOCK */
2397 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002398 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002399 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002400
Michal Vaskoe2713da2016-08-22 16:06:40 +02002401 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002402 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002403 /* BIND UNLOCK */
2404 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002405 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002406 if (!ret) {
2407 return NC_MSG_WOULDBLOCK;
2408 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002409 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002410 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002411
2412 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2413 /* ENDPT READ LOCK */
2414 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2415
2416 /* BIND UNLOCK */
2417 pthread_mutex_unlock(&server_opts.bind_lock);
2418
Michal Vaskob48aa812016-01-18 14:13:09 +01002419 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002420
Michal Vasko131120a2018-05-29 15:44:02 +02002421 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002422 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002423 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002424 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002425 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002426 msgtype = NC_MSG_ERROR;
2427 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002428 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002429 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002430 (*session)->ctx = server_opts.ctx;
2431 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko77367452021-02-16 16:32:18 +01002432 lydict_insert_zc(server_opts.ctx, host, &(*session)->host);
Michal Vasko1a38c862016-01-15 15:50:07 +01002433 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002434
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002435 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002436#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002437 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2438 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002439 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002440 if (ret < 0) {
2441 msgtype = NC_MSG_ERROR;
2442 goto cleanup;
2443 } else if (!ret) {
2444 msgtype = NC_MSG_WOULDBLOCK;
2445 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002446 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002447 } else
2448#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002449#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002450 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2451 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002452 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002453 if (ret < 0) {
2454 msgtype = NC_MSG_ERROR;
2455 goto cleanup;
2456 } else if (!ret) {
2457 msgtype = NC_MSG_WOULDBLOCK;
2458 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002459 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002460 } else
2461#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002462 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2463 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2464 ret = nc_accept_unix(*session, sock);
2465 if (ret < 0) {
2466 msgtype = NC_MSG_ERROR;
2467 goto cleanup;
2468 }
2469 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002470 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002471 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002472 msgtype = NC_MSG_ERROR;
2473 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002474 }
2475
Michal Vasko2cc4c682016-03-01 09:16:48 +01002476 (*session)->data = NULL;
2477
Michal Vaskoade892d2017-02-22 13:40:35 +01002478 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002479 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002480
Michal Vaskob48aa812016-01-18 14:13:09 +01002481 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002482 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002483
Michal Vasko9e036d52016-01-08 10:49:26 +01002484 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002485 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002486 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002487 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002488 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002489 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002490 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002491
2492 nc_gettimespec_mono(&ts_cur);
2493 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2494 nc_gettimespec_real(&ts_cur);
2495 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002496 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002497
Michal Vasko71090fc2016-05-24 16:37:28 +02002498 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002499
Michal Vasko71090fc2016-05-24 16:37:28 +02002500cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002501 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002502 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002503
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002504 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002505 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002506 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002507}
2508
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002509#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002510
Michal Vaskoadf30f02019-06-24 09:34:47 +02002511/* client is expected to be locked */
2512static int
2513_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 +02002514{
2515 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002516 int ret = -1;
2517
2518 if (!endpt_name) {
2519 /* remove all endpoints */
2520 for (i = 0; i < client->ch_endpt_count; ++i) {
2521 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2522 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2523 if (client->ch_endpts[i].sock_pending != -1) {
2524 close(client->ch_endpts[i].sock_pending);
2525 }
2526 switch (client->ch_endpts[i].ti) {
2527#ifdef NC_ENABLED_SSH
2528 case NC_TI_LIBSSH:
2529 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2530 free(client->ch_endpts[i].opts.ssh);
2531 break;
2532#endif
2533#ifdef NC_ENABLED_TLS
2534 case NC_TI_OPENSSL:
2535 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2536 free(client->ch_endpts[i].opts.tls);
2537 break;
2538#endif
2539 default:
2540 ERRINT;
2541 /* won't get here ...*/
2542 break;
2543 }
2544 }
2545 free(client->ch_endpts);
2546 client->ch_endpts = NULL;
2547 client->ch_endpt_count = 0;
2548
2549 ret = 0;
2550 } else {
2551 for (i = 0; i < client->ch_endpt_count; ++i) {
2552 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
2553 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2554 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2555 if (client->ch_endpts[i].sock_pending != -1) {
2556 close(client->ch_endpts[i].sock_pending);
2557 }
2558 switch (client->ch_endpts[i].ti) {
2559#ifdef NC_ENABLED_SSH
2560 case NC_TI_LIBSSH:
2561 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2562 free(client->ch_endpts[i].opts.ssh);
2563 break;
2564#endif
2565#ifdef NC_ENABLED_TLS
2566 case NC_TI_OPENSSL:
2567 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2568 free(client->ch_endpts[i].opts.tls);
2569 break;
2570#endif
2571 default:
2572 ERRINT;
2573 /* won't get here ...*/
2574 break;
2575 }
2576
2577 /* move last endpoint to the empty space */
2578 --client->ch_endpt_count;
2579 if (i < client->ch_endpt_count) {
2580 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2581 } else if (!server_opts.ch_client_count) {
2582 free(server_opts.ch_clients);
2583 server_opts.ch_clients = NULL;
2584 }
2585
2586 ret = 0;
2587 break;
2588 }
2589 }
2590 }
2591
2592 return ret;
2593}
2594
2595API int
2596nc_server_ch_add_client(const char *name)
2597{
2598 uint16_t i;
2599 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002600
2601 if (!name) {
2602 ERRARG("name");
2603 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002604 }
2605
2606 /* WRITE LOCK */
2607 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2608
2609 /* check name uniqueness */
2610 for (i = 0; i < server_opts.ch_client_count; ++i) {
2611 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002612 ERR(NULL, "Call Home client \"%s\" already exists.", name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002613 /* WRITE UNLOCK */
2614 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2615 return -1;
2616 }
2617 }
2618
2619 ++server_opts.ch_client_count;
2620 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2621 if (!server_opts.ch_clients) {
2622 ERRMEM;
2623 /* WRITE UNLOCK */
2624 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2625 return -1;
2626 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002627 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002628
Michal Vasko77367452021-02-16 16:32:18 +01002629 lydict_insert(server_opts.ctx, name, 0, &client->name);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002630 client->id = ATOMIC_INC_RELAXED(server_opts.new_client_id);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002631 client->ch_endpts = NULL;
2632 client->ch_endpt_count = 0;
2633 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002634
Michal Vasko2e6defd2016-10-07 15:48:15 +02002635 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002636 client->start_with = NC_CH_FIRST_LISTED;
2637 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002638
Michal Vaskoadf30f02019-06-24 09:34:47 +02002639 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002640
2641 /* WRITE UNLOCK */
2642 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2643
2644 return 0;
2645}
2646
2647API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002648nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002649{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002650 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002651 int ret = -1;
2652
2653 /* WRITE LOCK */
2654 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2655
Michal Vaskoadf30f02019-06-24 09:34:47 +02002656 if (!name) {
2657 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002658 for (i = 0; i < server_opts.ch_client_count; ++i) {
2659 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2660
2661 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002662 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002663
2664 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002665 ret = 0;
2666 }
2667 free(server_opts.ch_clients);
2668 server_opts.ch_clients = NULL;
2669
2670 server_opts.ch_client_count = 0;
2671
2672 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002673 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002674 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002675 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002676 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2677
Michal Vasko2e6defd2016-10-07 15:48:15 +02002678 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002679 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002680
2681 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2682
2683 /* move last client and endpoint(s) to the empty space */
2684 --server_opts.ch_client_count;
2685 if (i < server_opts.ch_client_count) {
2686 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002687 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002688 } else if (!server_opts.ch_client_count) {
2689 free(server_opts.ch_clients);
2690 server_opts.ch_clients = NULL;
2691 }
2692
2693 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002694 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002695 }
2696 }
2697 }
2698
2699 /* WRITE UNLOCK */
2700 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2701
2702 return ret;
2703}
2704
2705API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002706nc_server_ch_is_client(const char *name)
2707{
2708 uint16_t i;
2709 int found = 0;
2710
2711 if (!name) {
2712 return found;
2713 }
2714
2715 /* READ LOCK */
2716 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2717
2718 /* check name uniqueness */
2719 for (i = 0; i < server_opts.ch_client_count; ++i) {
2720 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2721 found = 1;
2722 break;
2723 }
2724 }
2725
2726 /* UNLOCK */
2727 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2728
2729 return found;
2730}
2731
2732API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002733nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002734{
2735 uint16_t i;
2736 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002737 struct nc_ch_endpt *endpt;
2738 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002739
2740 if (!client_name) {
2741 ERRARG("client_name");
2742 return -1;
2743 } else if (!endpt_name) {
2744 ERRARG("endpt_name");
2745 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002746 } else if (!ti) {
2747 ERRARG("ti");
2748 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002749 }
2750
2751 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002752 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002753 if (!client) {
2754 return -1;
2755 }
2756
2757 for (i = 0; i < client->ch_endpt_count; ++i) {
2758 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002759 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002760 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002761 }
2762 }
2763
2764 ++client->ch_endpt_count;
2765 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2766 if (!client->ch_endpts) {
2767 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002768 goto cleanup;
2769 }
2770 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2771
2772 memset(endpt, 0, sizeof *client->ch_endpts);
Michal Vasko77367452021-02-16 16:32:18 +01002773 lydict_insert(server_opts.ctx, endpt_name, 0, &endpt->name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002774 endpt->ti = ti;
2775 endpt->sock_pending = -1;
2776 endpt->ka.idle_time = 1;
2777 endpt->ka.max_probes = 10;
2778 endpt->ka.probe_interval = 5;
2779
2780 switch (ti) {
2781#ifdef NC_ENABLED_SSH
2782 case NC_TI_LIBSSH:
2783 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2784 if (!endpt->opts.ssh) {
2785 ERRMEM;
2786 goto cleanup;
2787 }
2788 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2789 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002790 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002791 break;
2792#endif
2793#ifdef NC_ENABLED_TLS
2794 case NC_TI_OPENSSL:
2795 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2796 if (!endpt->opts.tls) {
2797 ERRMEM;
2798 goto cleanup;
2799 }
2800 break;
2801#endif
2802 default:
2803 ERRINT;
2804 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002805 }
2806
Michal Vaskoadf30f02019-06-24 09:34:47 +02002807 /* success */
2808 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002809
Michal Vaskoadf30f02019-06-24 09:34:47 +02002810cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002811 /* UNLOCK */
2812 nc_server_ch_client_unlock(client);
2813
Michal Vaskoadf30f02019-06-24 09:34:47 +02002814 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002815}
2816
2817API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002818nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002819{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002820 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002821 struct nc_ch_client *client;
2822
2823 if (!client_name) {
2824 ERRARG("client_name");
2825 return -1;
2826 }
2827
2828 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002829 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002830 if (!client) {
2831 return -1;
2832 }
2833
Michal Vaskoadf30f02019-06-24 09:34:47 +02002834 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002835
2836 /* UNLOCK */
2837 nc_server_ch_client_unlock(client);
2838
2839 return ret;
2840}
2841
2842API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002843nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2844{
2845 uint16_t i;
2846 struct nc_ch_client *client = NULL;
2847 int found = 0;
2848
2849 if (!client_name || !endpt_name) {
2850 return found;
2851 }
2852
2853 /* READ LOCK */
2854 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2855
2856 for (i = 0; i < server_opts.ch_client_count; ++i) {
2857 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2858 client = &server_opts.ch_clients[i];
2859 break;
2860 }
2861 }
2862
2863 if (!client) {
2864 goto cleanup;
2865 }
2866
2867 for (i = 0; i < client->ch_endpt_count; ++i) {
2868 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2869 found = 1;
2870 break;
2871 }
2872 }
2873
2874cleanup:
2875 /* UNLOCK */
2876 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2877 return found;
2878}
2879
2880API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002881nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2882{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002883 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002884 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002885
2886 if (!client_name) {
2887 ERRARG("client_name");
2888 return -1;
2889 } else if (!endpt_name) {
2890 ERRARG("endpt_name");
2891 return -1;
2892 } else if (!address) {
2893 ERRARG("address");
2894 return -1;
2895 }
2896
2897 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002898 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2899 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002900 return -1;
2901 }
2902
Michal Vaskoadf30f02019-06-24 09:34:47 +02002903 lydict_remove(server_opts.ctx, endpt->address);
Michal Vasko77367452021-02-16 16:32:18 +01002904 lydict_insert(server_opts.ctx, address, 0, &endpt->address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002905
2906 /* UNLOCK */
2907 nc_server_ch_client_unlock(client);
2908
Michal Vaskoadf30f02019-06-24 09:34:47 +02002909 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002910}
2911
2912API int
2913nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2914{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002915 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002916 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002917
2918 if (!client_name) {
2919 ERRARG("client_name");
2920 return -1;
2921 } else if (!endpt_name) {
2922 ERRARG("endpt_name");
2923 return -1;
2924 } else if (!port) {
2925 ERRARG("port");
2926 return -1;
2927 }
2928
2929 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002930 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2931 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002932 return -1;
2933 }
2934
Michal Vaskoadf30f02019-06-24 09:34:47 +02002935 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002936
2937 /* UNLOCK */
2938 nc_server_ch_client_unlock(client);
2939
ravsz5c5a4422020-03-31 15:53:21 +02002940 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002941}
2942
2943API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002944nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
2945{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002946 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002947 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002948
2949 if (!client_name) {
2950 ERRARG("client_name");
2951 return -1;
2952 } else if (!endpt_name) {
2953 ERRARG("endpt_name");
2954 return -1;
2955 }
2956
2957 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002958 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2959 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002960 return -1;
2961 }
2962
Michal Vaskoadf30f02019-06-24 09:34:47 +02002963 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002964
2965 /* UNLOCK */
2966 nc_server_ch_client_unlock(client);
2967
Michal Vasko9af829a2019-09-12 13:50:00 +02002968 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002969}
2970
2971API int
2972nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
2973 int probe_interval)
2974{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002975 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002976 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002977
2978 if (!client_name) {
2979 ERRARG("client_name");
2980 return -1;
2981 } else if (!endpt_name) {
2982 ERRARG("endpt_name");
2983 return -1;
2984 }
2985
2986 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002987 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2988 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002989 return -1;
2990 }
2991
Michal Vaskoadf30f02019-06-24 09:34:47 +02002992 if (idle_time > -1) {
2993 endpt->ka.idle_time = idle_time;
2994 }
2995 if (max_probes > -1) {
2996 endpt->ka.max_probes = max_probes;
2997 }
2998 if (probe_interval > -1) {
2999 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003000 }
3001
3002 /* UNLOCK */
3003 nc_server_ch_client_unlock(client);
3004
Michal Vasko9af829a2019-09-12 13:50:00 +02003005 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003006}
3007
3008API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003009nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
3010{
3011 struct nc_ch_client *client;
3012
3013 if (!client_name) {
3014 ERRARG("client_name");
3015 return -1;
3016 } else if (!conn_type) {
3017 ERRARG("conn_type");
3018 return -1;
3019 }
3020
3021 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003022 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003023 if (!client) {
3024 return -1;
3025 }
3026
3027 if (client->conn_type != conn_type) {
3028 client->conn_type = conn_type;
3029
3030 /* set default options */
3031 switch (conn_type) {
3032 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003033 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003034 break;
3035 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003036 client->conn.period.period = 60;
3037 client->conn.period.anchor_time = 0;
3038 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003039 break;
3040 default:
3041 ERRINT;
3042 break;
3043 }
3044 }
3045
3046 /* UNLOCK */
3047 nc_server_ch_client_unlock(client);
3048
3049 return 0;
3050}
3051
3052API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003053nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
3054{
3055 struct nc_ch_client *client;
3056
3057 if (!client_name) {
3058 ERRARG("client_name");
3059 return -1;
3060 } else if (!period) {
3061 ERRARG("period");
3062 return -1;
3063 }
3064
3065 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003066 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003067 if (!client) {
3068 return -1;
3069 }
3070
3071 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003072 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003073 /* UNLOCK */
3074 nc_server_ch_client_unlock(client);
3075 return -1;
3076 }
3077
3078 client->conn.period.period = period;
3079
3080 /* UNLOCK */
3081 nc_server_ch_client_unlock(client);
3082
3083 return 0;
3084}
3085
3086API int
3087nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003088{
3089 struct nc_ch_client *client;
3090
3091 if (!client_name) {
3092 ERRARG("client_name");
3093 return -1;
3094 }
3095
3096 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003097 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003098 if (!client) {
3099 return -1;
3100 }
3101
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003102 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003103 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003104 /* UNLOCK */
3105 nc_server_ch_client_unlock(client);
3106 return -1;
3107 }
3108
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003109 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003110
3111 /* UNLOCK */
3112 nc_server_ch_client_unlock(client);
3113
3114 return 0;
3115}
3116
3117API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003118nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003119{
3120 struct nc_ch_client *client;
3121
3122 if (!client_name) {
3123 ERRARG("client_name");
3124 return -1;
3125 }
3126
3127 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003128 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003129 if (!client) {
3130 return -1;
3131 }
3132
3133 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003134 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003135 /* UNLOCK */
3136 nc_server_ch_client_unlock(client);
3137 return -1;
3138 }
3139
3140 client->conn.period.idle_timeout = idle_timeout;
3141
3142 /* UNLOCK */
3143 nc_server_ch_client_unlock(client);
3144
3145 return 0;
3146}
3147
3148API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003149nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
3150{
3151 struct nc_ch_client *client;
3152
3153 if (!client_name) {
3154 ERRARG("client_name");
3155 return -1;
3156 }
3157
3158 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003159 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003160 if (!client) {
3161 return -1;
3162 }
3163
3164 client->start_with = start_with;
3165
3166 /* UNLOCK */
3167 nc_server_ch_client_unlock(client);
3168
3169 return 0;
3170}
3171
3172API int
3173nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
3174{
3175 struct nc_ch_client *client;
3176
3177 if (!client_name) {
3178 ERRARG("client_name");
3179 return -1;
3180 } else if (!max_attempts) {
3181 ERRARG("max_attempts");
3182 return -1;
3183 }
3184
3185 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003186 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003187 if (!client) {
3188 return -1;
3189 }
3190
3191 client->max_attempts = max_attempts;
3192
3193 /* UNLOCK */
3194 nc_server_ch_client_unlock(client);
3195
3196 return 0;
3197}
3198
3199/* client lock is expected to be held */
3200static NC_MSG_TYPE
Michal Vaskoadf30f02019-06-24 09:34:47 +02003201nc_connect_ch_endpt(struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01003202{
Michal Vasko71090fc2016-05-24 16:37:28 +02003203 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003204 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02003205 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01003206 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01003207
Michal Vasko4c612cd2021-02-05 08:53:42 +01003208 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 +01003209 if (sock < 0) {
Michal Vasko0b30e452021-03-03 10:30:15 +01003210 if (endpt->sock_pending > -1) {
3211 ++endpt->sock_retries;
3212 if (endpt->sock_retries == NC_SOCKET_CH_RETRIES) {
Michal Vasko05532772021-06-03 12:12:38 +02003213 ERR(NULL, "Failed to connect socket %d after %d retries, closing.", endpt->sock_pending, NC_SOCKET_CH_RETRIES);
Michal Vasko0b30e452021-03-03 10:30:15 +01003214 close(endpt->sock_pending);
3215 endpt->sock_pending = -1;
3216 endpt->sock_retries = 0;
3217 }
Michal Vasko4c612cd2021-02-05 08:53:42 +01003218 }
Michal Vasko71090fc2016-05-24 16:37:28 +02003219 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003220 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00003221 /* no need to store the socket as pending any longer */
3222 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01003223
Michal Vasko131120a2018-05-29 15:44:02 +02003224 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01003225 if (!(*session)) {
3226 ERRMEM;
3227 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01003228 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02003229 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003230 }
3231 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01003232 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003233 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko77367452021-02-16 16:32:18 +01003234 lydict_insert_zc(server_opts.ctx, ip_host, &(*session)->host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003235 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01003236
Michal Vaskob05053d2016-01-22 16:12:06 +01003237 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01003238#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02003239 if (endpt->ti == NC_TI_LIBSSH) {
3240 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01003241 ret = nc_accept_ssh_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
Radek Krejci53691be2016-02-22 13:58:37 +01003253#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02003254 if (endpt->ti == NC_TI_OPENSSL) {
3255 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01003256 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003257 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003258
Michal Vasko71090fc2016-05-24 16:37:28 +02003259 if (ret < 0) {
3260 msgtype = NC_MSG_ERROR;
3261 goto fail;
3262 } else if (!ret) {
3263 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003264 goto fail;
3265 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003266 } else
3267#endif
3268 {
Michal Vaskob05053d2016-01-22 16:12:06 +01003269 ERRINT;
3270 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003271 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003272 goto fail;
3273 }
3274
3275 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02003276 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003277
3278 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003279 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003280 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003281 goto fail;
3282 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003283
3284 nc_gettimespec_mono(&ts_cur);
3285 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
3286 nc_gettimespec_real(&ts_cur);
3287 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003288 (*session)->status = NC_STATUS_RUNNING;
3289
Michal Vasko71090fc2016-05-24 16:37:28 +02003290 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003291
3292fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003293 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003294 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02003295 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003296}
3297
Michal Vasko2e6defd2016-10-07 15:48:15 +02003298struct nc_ch_client_thread_arg {
3299 char *client_name;
Michal Vaskof1c26c22021-04-12 16:34:33 +02003300 int (*session_clb)(const char *client_name, struct nc_session *new_session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003301};
3302
3303static struct nc_ch_client *
3304nc_server_ch_client_with_endpt_lock(const char *name)
3305{
3306 struct nc_ch_client *client;
3307
3308 while (1) {
3309 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003310 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003311 if (!client) {
3312 return NULL;
3313 }
3314 if (client->ch_endpt_count) {
3315 return client;
3316 }
3317 /* no endpoints defined yet */
3318
3319 /* UNLOCK */
3320 nc_server_ch_client_unlock(client);
3321
3322 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3323 }
3324
3325 return NULL;
3326}
3327
3328static int
3329nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3330{
Michal Vasko3f05a092018-03-13 10:39:49 +01003331 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003332 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003333 struct timespec ts;
3334 struct nc_ch_client *client;
3335
Michal Vasko2e6defd2016-10-07 15:48:15 +02003336 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003337 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003338
Michal Vasko0db3db52021-03-03 10:45:42 +01003339 session->flags |= NC_SESSION_CALLHOME;
3340
Michal Vasko2e6defd2016-10-07 15:48:15 +02003341 /* give the session to the user */
Michal Vaskof1c26c22021-04-12 16:34:33 +02003342 if (data->session_clb(data->client_name, session)) {
3343 /* something is wrong, free the session */
3344 session->flags &= ~NC_SESSION_CALLHOME;
3345
3346 /* CH UNLOCK */
3347 pthread_mutex_unlock(&session->opts.server.ch_lock);
3348
3349 nc_session_free(session, NULL);
3350 return 0;
3351 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003352
3353 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003354 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003355 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003356
Michal Vasko0db3db52021-03-03 10:45:42 +01003357 /* CH COND WAIT */
Michal Vaskoacf98472021-02-04 15:33:57 +01003358 r = pthread_cond_timedwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, &ts);
Michal Vasko3f05a092018-03-13 10:39:49 +01003359 if (!r) {
3360 /* we were woken up, something probably happened */
3361 if (session->status != NC_STATUS_RUNNING) {
3362 break;
3363 }
3364 } else if (r != ETIMEDOUT) {
Michal Vasko05532772021-06-03 12:12:38 +02003365 ERR(session, "Pthread condition timedwait failed (%s).", strerror(r));
Michal Vasko3f05a092018-03-13 10:39:49 +01003366 ret = -1;
3367 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003368 }
3369
Michal Vasko2e6defd2016-10-07 15:48:15 +02003370 /* check whether the client was not removed */
3371 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003372 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003373 if (!client) {
3374 /* client was removed, finish thread */
Michal Vasko05532772021-06-03 12:12:38 +02003375 VRB(session, "Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003376 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003377 ret = 1;
3378 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003379 }
3380
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003381 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003382 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003383 } else {
3384 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003385 }
3386
Michal Vasko9fb42272017-10-05 13:50:05 +02003387 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003388 if (!session->opts.server.ntf_status && idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
Michal Vasko05532772021-06-03 12:12:38 +02003389 VRB(session, "Call Home client \"%s\": session idle timeout elapsed.", client->name);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003390 session->status = NC_STATUS_INVALID;
3391 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3392 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003393
3394 /* UNLOCK */
3395 nc_server_ch_client_unlock(client);
3396
3397 } while (session->status == NC_STATUS_RUNNING);
3398
Michal Vasko0db3db52021-03-03 10:45:42 +01003399 /* signal to nc_session_free() that CH registered this session not being valid anymore */
3400 session->flags &= ~NC_SESSION_CALLHOME;
3401
Michal Vasko27377422018-03-15 08:59:35 +01003402 /* CH UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003403 pthread_mutex_unlock(&session->opts.server.ch_lock);
Michal Vasko27377422018-03-15 08:59:35 +01003404
Michal Vasko3f05a092018-03-13 10:39:49 +01003405 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003406}
3407
3408static void *
3409nc_ch_client_thread(void *arg)
3410{
3411 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3412 NC_MSG_TYPE msgtype;
3413 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003414 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003415 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003416 struct nc_ch_endpt *cur_endpt;
3417 struct nc_session *session;
3418 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003419 uint32_t client_id;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003420 time_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003421
3422 /* LOCK */
3423 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3424 if (!client) {
3425 goto cleanup;
3426 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003427 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003428
3429 cur_endpt = &client->ch_endpts[0];
3430 cur_endpt_name = strdup(cur_endpt->name);
3431
Michal Vasko05532772021-06-03 12:12:38 +02003432 VRB(NULL, "Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003433 while (1) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02003434 msgtype = nc_connect_ch_endpt(cur_endpt, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003435
3436 if (msgtype == NC_MSG_HELLO) {
3437 /* UNLOCK */
3438 nc_server_ch_client_unlock(client);
3439
Michal Vasko05532772021-06-03 12:12:38 +02003440 VRB(NULL, "Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003441 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3442 goto cleanup;
3443 }
Michal Vasko05532772021-06-03 12:12:38 +02003444 VRB(NULL, "Call Home client \"%s\" session terminated.", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003445
3446 /* LOCK */
3447 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3448 if (!client) {
3449 goto cleanup;
3450 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003451 if (client->id != client_id) {
3452 nc_server_ch_client_unlock(client);
3453 goto cleanup;
3454 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003455
3456 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003457 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003458 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003459 /* UNLOCK */
3460 nc_server_ch_client_unlock(client);
3461
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003462 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
3463 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
Michal Vasko05532772021-06-03 12:12:38 +02003464 VRB(NULL, "Call Home client \"%s\" reconnecting in %d seconds.", data->client_name, reconnect_in);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003465 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003466
3467 /* LOCK */
3468 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3469 if (!client) {
3470 goto cleanup;
3471 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003472 if (client->id != client_id) {
3473 nc_server_ch_client_unlock(client);
3474 goto cleanup;
3475 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003476 }
3477
3478 /* set next endpoint to try */
3479 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003480 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003481 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003482 /* we keep the current one but due to unlock/lock we have to find it again */
3483 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3484 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3485 break;
3486 }
3487 }
3488 if (next_endpt_index >= client->ch_endpt_count) {
3489 /* endpoint was removed, start with the first one */
3490 next_endpt_index = 0;
3491 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003492 } else {
3493 /* just get a random index */
3494 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003495 }
3496
Michal Vasko2e6defd2016-10-07 15:48:15 +02003497 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003498 /* UNLOCK */
3499 nc_server_ch_client_unlock(client);
3500
Michal Vasko2e6defd2016-10-07 15:48:15 +02003501 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003502 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3503
Michal Vasko6bb116b2016-10-26 13:53:46 +02003504 /* LOCK */
3505 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3506 if (!client) {
3507 goto cleanup;
3508 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003509 if (client->id != client_id) {
3510 nc_server_ch_client_unlock(client);
3511 goto cleanup;
3512 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003513
Michal Vasko2e6defd2016-10-07 15:48:15 +02003514 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003515
3516 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003517 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3518 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003519 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003520 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003521 }
3522
Peter Feiged05f2252018-09-03 08:09:47 +00003523 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003524 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003525 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003526 cur_attempts = 0;
3527 } else if (cur_attempts == client->max_attempts) {
3528 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003529 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003530 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003531 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003532 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003533 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003534 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003535 }
3536
3537 cur_attempts = 0;
3538 } /* else we keep the current one */
3539 }
Peter Feiged05f2252018-09-03 08:09:47 +00003540
3541 cur_endpt = &client->ch_endpts[next_endpt_index];
3542 free(cur_endpt_name);
3543 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003544 }
3545
3546cleanup:
Michal Vasko05532772021-06-03 12:12:38 +02003547 VRB(NULL, "Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003548 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003549 free(data->client_name);
3550 free(data);
3551 return NULL;
3552}
3553
3554API int
Michal Vaskof1c26c22021-04-12 16:34:33 +02003555nc_connect_ch_client_dispatch(const char *client_name, int (*session_clb)(const char *client_name,
Michal Vaskoadf30f02019-06-24 09:34:47 +02003556 struct nc_session *new_session))
Michal Vasko3f05a092018-03-13 10:39:49 +01003557{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003558 int ret;
3559 pthread_t tid;
3560 struct nc_ch_client_thread_arg *arg;
3561
3562 if (!client_name) {
3563 ERRARG("client_name");
3564 return -1;
3565 } else if (!session_clb) {
3566 ERRARG("session_clb");
3567 return -1;
3568 }
3569
3570 arg = malloc(sizeof *arg);
3571 if (!arg) {
3572 ERRMEM;
3573 return -1;
3574 }
3575 arg->client_name = strdup(client_name);
3576 if (!arg->client_name) {
3577 ERRMEM;
3578 free(arg);
3579 return -1;
3580 }
3581 arg->session_clb = session_clb;
3582
3583 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3584 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02003585 ERR(NULL, "Creating a new thread failed (%s).", strerror(ret));
Michal Vasko2e6defd2016-10-07 15:48:15 +02003586 free(arg->client_name);
3587 free(arg);
3588 return -1;
3589 }
3590 /* the thread now manages arg */
3591
3592 pthread_detach(tid);
3593
3594 return 0;
3595}
3596
Radek Krejci53691be2016-02-22 13:58:37 +01003597#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003598
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003599API time_t
3600nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003601{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003602 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003603 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003604 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003605 }
3606
Michal Vasko2e6defd2016-10-07 15:48:15 +02003607 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003608}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003609
3610API void
Michal Vasko71dbd772021-03-23 14:08:37 +01003611nc_session_inc_notif_status(struct nc_session *session)
Michal Vasko3486a7c2017-03-03 13:28:07 +01003612{
3613 if (!session || (session->side != NC_SERVER)) {
3614 ERRARG("session");
3615 return;
3616 }
3617
Michal Vasko71dbd772021-03-23 14:08:37 +01003618 ++session->opts.server.ntf_status;
3619}
3620
3621API void
3622nc_session_dec_notif_status(struct nc_session *session)
3623{
3624 if (!session || (session->side != NC_SERVER)) {
3625 ERRARG("session");
3626 return;
3627 }
3628
3629 if (session->opts.server.ntf_status) {
3630 --session->opts.server.ntf_status;
3631 }
Michal Vasko3486a7c2017-03-03 13:28:07 +01003632}
3633
3634API int
3635nc_session_get_notif_status(const struct nc_session *session)
3636{
3637 if (!session || (session->side != NC_SERVER)) {
3638 ERRARG("session");
3639 return 0;
3640 }
3641
3642 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003643}
Michal Vasko8f430592019-02-26 08:32:54 +01003644
3645API int
3646nc_session_is_callhome(const struct nc_session *session)
3647{
3648 if (!session || (session->side != NC_SERVER)) {
3649 ERRARG("session");
3650 return 0;
3651 }
3652
3653 if (session->flags & NC_SESSION_CALLHOME) {
3654 return 1;
3655 }
3656
3657 return 0;
3658}