blob: 546050ced74459b3964e2ed4adb0874bdc6cc689 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_server.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libnetconf2 server session manipulation functions
Michal Vasko086311b2016-01-08 09:53:11 +01005 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01006 * @copyright
7 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01008 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01009 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010012 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010013 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010014 */
apropp-molex4e903c32020-04-20 03:06:58 -040015#define _QNX_SOURCE /* getpeereid */
Olivier Matzac7fa2f2018-10-11 10:02:04 +020016#define _GNU_SOURCE /* signals, threads, SO_PEERCRED */
Michal Vasko086311b2016-01-08 09:53:11 +010017
Michal Vaskob83a3fa2021-05-26 09:53:42 +020018#include <arpa/inet.h>
Michal Vasko086311b2016-01-08 09:53:11 +010019#include <errno.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020020#include <fcntl.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020021#include <netinet/in.h>
22#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020023#include <poll.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010024#include <pthread.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020025#include <pwd.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020026#include <signal.h>
27#include <stdint.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/socket.h>
31#include <sys/types.h>
32#include <sys/un.h>
33#include <time.h>
34#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010035
Michal Vasko7a20d2e2021-05-19 16:40:23 +020036#include "compat.h"
Michal Vasko1a38c862016-01-15 15:50:07 +010037#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010038#include "session_server.h"
Michal Vasko0bdf70b2019-06-24 19:20:20 +020039#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010040
Michal Vaskob48aa812016-01-18 14:13:09 +010041struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010042#ifdef NC_ENABLED_SSH
43 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
44#endif
45 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020046 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
47 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010048};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010049
fanchanghu966f2de2016-07-21 02:28:57 -040050static nc_rpc_clb global_rpc_clb = NULL;
51
Michal Vasko3031aae2016-01-27 16:07:18 +010052struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010053nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010054{
55 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010056 struct nc_endpt *endpt = NULL;
57
Michal Vaskoddce1212019-05-24 09:58:49 +020058 if (!name) {
59 ERRARG("endpt_name");
60 return NULL;
61 }
62
Michal Vaskoade892d2017-02-22 13:40:35 +010063 /* WRITE LOCK */
64 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010065
66 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020067 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010068 endpt = &server_opts.endpts[i];
69 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010070 }
71 }
72
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010073 if (!endpt) {
Michal Vasko05532772021-06-03 12:12:38 +020074 ERR(NULL, "Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010075 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020076 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010077 return NULL;
78 }
79
Michal Vaskoe2713da2016-08-22 16:06:40 +020080 if (idx) {
81 *idx = i;
82 }
83
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010084 return endpt;
85}
86
Michal Vaskoadf30f02019-06-24 09:34:47 +020087struct nc_ch_endpt *
88nc_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 +010089{
Michal Vaskoadf30f02019-06-24 09:34:47 +020090 uint16_t i, j;
Michal Vasko2e6defd2016-10-07 15:48:15 +020091 struct nc_ch_client *client = NULL;
Michal Vaskoadf30f02019-06-24 09:34:47 +020092 struct nc_ch_endpt *endpt = NULL;
93
94 *client_p = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +020095
Michal Vaskoddce1212019-05-24 09:58:49 +020096 if (!name) {
97 ERRARG("client_name");
98 return NULL;
99 }
100
Michal Vasko2e6defd2016-10-07 15:48:15 +0200101 /* READ LOCK */
102 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
103
104 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200105 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200106 client = &server_opts.ch_clients[i];
Michal Vaskoadf30f02019-06-24 09:34:47 +0200107 if (!endpt_name && !ti) {
108 /* return only client */
109 break;
110 }
111 for (j = 0; j < client->ch_endpt_count; ++j) {
Michal Vasko530d95c2021-05-28 13:32:02 +0200112 if ((!endpt_name || !strcmp(client->ch_endpts[j].name, endpt_name)) &&
113 (!ti || (ti == client->ch_endpts[j].ti))) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200114 endpt = &client->ch_endpts[j];
115 break;
116 }
117 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200118 break;
119 }
120 }
121
122 if (!client) {
Michal Vasko05532772021-06-03 12:12:38 +0200123 ERR(NULL, "Call Home client \"%s\" was not found.", name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200124
Michal Vasko2e6defd2016-10-07 15:48:15 +0200125 /* READ UNLOCK */
126 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200127 } else if (endpt_name && ti && !endpt) {
Michal Vasko05532772021-06-03 12:12:38 +0200128 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" was not found.", name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200129
130 /* READ UNLOCK */
131 pthread_rwlock_unlock(&server_opts.ch_client_lock);
132 } else {
133 /* CH CLIENT LOCK */
134 pthread_mutex_lock(&client->lock);
135
136 *client_p = client;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200137 }
138
Michal Vaskoadf30f02019-06-24 09:34:47 +0200139 return endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200140}
141
142void
143nc_server_ch_client_unlock(struct nc_ch_client *client)
144{
145 /* CH CLIENT UNLOCK */
146 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100147
148 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200149 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100150}
Michal Vasko086311b2016-01-08 09:53:11 +0100151
Michal Vasko1a38c862016-01-15 15:50:07 +0100152API void
153nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
154{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200155 if (!session) {
156 ERRARG("session");
157 return;
158 } else if (!reason) {
159 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100160 return;
161 }
162
Michal Vasko142cfea2017-08-07 10:12:11 +0200163 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
164 session->killed_by = 0;
165 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100166 session->term_reason = reason;
167}
168
Michal Vasko142cfea2017-08-07 10:12:11 +0200169API void
170nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
171{
172 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
173 ERRARG("session");
174 return;
175 } else if (!sid) {
176 ERRARG("sid");
177 return;
178 }
179
180 session->killed_by = sid;
181}
182
183API void
184nc_session_set_status(struct nc_session *session, NC_STATUS status)
185{
186 if (!session) {
187 ERRARG("session");
188 return;
189 } else if (!status) {
190 ERRARG("status");
191 return;
192 }
193
194 session->status = status;
195}
196
Michal Vasko086311b2016-01-08 09:53:11 +0100197int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200198nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100199{
Michal Vasko06c860d2018-07-09 16:08:52 +0200200 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100201 int is_ipv4, sock;
202 struct sockaddr_storage saddr;
203
204 struct sockaddr_in *saddr4;
205 struct sockaddr_in6 *saddr6;
206
Michal Vasko086311b2016-01-08 09:53:11 +0100207 if (!strchr(address, ':')) {
208 is_ipv4 = 1;
209 } else {
210 is_ipv4 = 0;
211 }
212
213 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
214 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200215 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100216 goto fail;
217 }
218
Michal Vaskobe52dc22018-10-17 09:28:17 +0200219 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200220 opt = 1;
221 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200222 ERR(NULL, "Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko06c860d2018-07-09 16:08:52 +0200223 goto fail;
224 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100225 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200226 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vasko83ad17e2019-01-30 10:11:37 +0100227 goto fail;
228 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200229
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200230 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100231 goto fail;
232 }
233
Michal Vaskof22d5ff2020-04-15 11:10:27 +0200234 memset(&saddr, 0, sizeof(struct sockaddr_storage));
Michal Vasko086311b2016-01-08 09:53:11 +0100235 if (is_ipv4) {
236 saddr4 = (struct sockaddr_in *)&saddr;
237
238 saddr4->sin_family = AF_INET;
239 saddr4->sin_port = htons(port);
240
241 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200242 ERR(NULL, "Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100243 goto fail;
244 }
245
246 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200247 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100248 goto fail;
249 }
250
251 } else {
252 saddr6 = (struct sockaddr_in6 *)&saddr;
253
254 saddr6->sin6_family = AF_INET6;
255 saddr6->sin6_port = htons(port);
256
257 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200258 ERR(NULL, "Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100259 goto fail;
260 }
261
262 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200263 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100264 goto fail;
265 }
266 }
267
Michal Vaskofb89d772016-01-08 12:25:35 +0100268 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200269 ERR(NULL, "Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100270 goto fail;
271 }
272
273 return sock;
274
275fail:
276 if (sock > -1) {
277 close(sock);
278 }
279
280 return -1;
281}
282
283int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200284nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
285{
286 struct sockaddr_un sun;
287 int sock = -1;
288
Michal Vasko93e96f12021-09-30 10:02:09 +0200289 if (strlen(address) > sizeof(sun.sun_path) - 1) {
290 ERR(NULL, "Socket path \"%s\" is longer than maximum length %d.", address, (int)(sizeof(sun.sun_path) - 1));
291 goto fail;
292 }
293
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200294 sock = socket(AF_UNIX, SOCK_STREAM, 0);
295 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200296 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200297 goto fail;
298 }
299
300 memset(&sun, 0, sizeof(sun));
301 sun.sun_family = AF_UNIX;
Michal Vasko93e96f12021-09-30 10:02:09 +0200302 snprintf(sun.sun_path, sizeof(sun.sun_path) - 1, "%s", address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200303
304 unlink(sun.sun_path);
305 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200306 ERR(NULL, "Could not bind \"%s\" (%s).", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200307 goto fail;
308 }
309
310 if (opts->mode != (mode_t)-1) {
311 if (chmod(sun.sun_path, opts->mode) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200312 ERR(NULL, "Failed to set unix socket permissions (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200313 goto fail;
314 }
315 }
316
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200317 if ((opts->uid != (uid_t)-1) || (opts->gid != (gid_t)-1)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200318 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200319 ERR(NULL, "Failed to set unix socket uid/gid (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200320 goto fail;
321 }
322 }
323
324 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200325 ERR(NULL, "Unable to start listening on \"%s\" (%s).", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200326 goto fail;
327 }
328
329 return sock;
330
331fail:
332 if (sock > -1) {
333 close(sock);
334 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200335 return -1;
336}
337
aPiecek90ff0242021-02-14 14:58:01 +0100338/**
339 * @brief Evaluate socket name for AF_UNIX socket.
340 * @param[in] acc_sock_fd is file descriptor for the accepted socket (a nonnegative).
341 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
342 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
343 * @return 0 if the stream socket is unnamed. Parameter host is set to NULL.
344 * @return -1 in case of error. Parameter host is set to NULL.
345 */
346static int
347sock_host_unix(int acc_sock_fd, char **host)
348{
349 char *sun_path;
350 struct sockaddr_storage saddr;
351 socklen_t addr_len;
352
353 *host = NULL;
354 saddr.ss_family = AF_UNIX;
355 addr_len = sizeof(saddr);
356
357 if (getsockname(acc_sock_fd, (struct sockaddr *)&saddr, &addr_len)) {
Michal Vasko05532772021-06-03 12:12:38 +0200358 ERR(NULL, "getsockname failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100359 return -1;
360 }
361
362 sun_path = ((struct sockaddr_un *)&saddr)->sun_path;
363 if (!sun_path) {
364 /* stream socket is unnamed */
365 return 0;
366 }
367
368 if (!(*host = strdup(sun_path))) {
369 ERRMEM;
370 return -1;
371 }
372
373 return 0;
374}
375
376/**
377 * @brief Evaluate socket name and port number for AF_INET socket.
378 * @param[in] addr is pointing to structure filled by accept function which was successful.
379 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
380 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
381 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
382 * @return -1 in case of error. Parameter host is set to NULL and port is unchanged.
383 */
384static int
385sock_host_inet(const struct sockaddr_in *addr, char **host, uint16_t *port)
386{
387 *host = malloc(INET_ADDRSTRLEN);
388 if (!(*host)) {
389 ERRMEM;
390 return -1;
391 }
392
aPiecek3da9b342021-02-18 15:00:03 +0100393 if (!inet_ntop(AF_INET, &addr->sin_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +0200394 ERR(NULL, "inet_ntop failed(%s).");
aPiecek90ff0242021-02-14 14:58:01 +0100395 free(*host);
396 *host = NULL;
397 return -1;
398 }
399
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200400 *port = ntohs(addr->sin_port);
aPiecek90ff0242021-02-14 14:58:01 +0100401
402 return 0;
403}
404
405/**
406 * @brief Evaluate socket name and port number for AF_INET6 socket.
407 * @param[in] addr is pointing to structure filled by accept function which was successful.
408 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
409 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
410 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
411 * @return -1 in case of error. Parameter host is set to the NULL and port is unchanged.
412 */
413static int
414sock_host_inet6(const struct sockaddr_in6 *addr, char **host, uint16_t *port)
415{
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200416 *host = malloc(INET6_ADDRSTRLEN);
aPiecek90ff0242021-02-14 14:58:01 +0100417 if (!(*host)) {
418 ERRMEM;
419 return -1;
420 }
421
aPiecek3da9b342021-02-18 15:00:03 +0100422 if (!inet_ntop(AF_INET6, &addr->sin6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +0200423 ERR(NULL, "inet_ntop failed(%s).");
aPiecek90ff0242021-02-14 14:58:01 +0100424 free(*host);
425 *host = NULL;
426 return -1;
427 }
428
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200429 *port = ntohs(addr->sin6_port);
aPiecek90ff0242021-02-14 14:58:01 +0100430
431 return 0;
432}
433
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200434int
Michal Vasko3031aae2016-01-27 16:07:18 +0100435nc_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 +0100436{
Michal Vaskof54cd352017-02-22 13:42:02 +0100437 sigset_t sigmask, origmask;
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200438 uint16_t i, j, pfd_count, client_port;
439 char *client_address;
Michal Vasko086311b2016-01-08 09:53:11 +0100440 struct pollfd *pfd;
441 struct sockaddr_storage saddr;
442 socklen_t saddr_len = sizeof(saddr);
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200443 int ret, client_sock, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100444
445 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100446 if (!pfd) {
447 ERRMEM;
448 return -1;
449 }
450
Michal Vaskoac2f6182017-01-30 14:32:03 +0100451 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200452 if (binds[i].sock < 0) {
453 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200454 continue;
455 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200456 if (binds[i].pollin) {
457 binds[i].pollin = 0;
458 /* leftover pollin */
459 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100460 break;
461 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100462 pfd[pfd_count].fd = binds[i].sock;
463 pfd[pfd_count].events = POLLIN;
464 pfd[pfd_count].revents = 0;
465
466 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100467 }
468
Michal Vasko0a3f3752016-10-13 14:58:38 +0200469 if (sock == -1) {
470 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100471 sigfillset(&sigmask);
472 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100473 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100474 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
475
Michal Vasko0a3f3752016-10-13 14:58:38 +0200476 if (!ret) {
477 /* we timeouted */
478 free(pfd);
479 return 0;
480 } else if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200481 ERR(NULL, "Poll failed (%s).", strerror(errno));
Michal Vasko0a3f3752016-10-13 14:58:38 +0200482 free(pfd);
483 return -1;
484 }
Michal Vasko086311b2016-01-08 09:53:11 +0100485
Michal Vaskoac2f6182017-01-30 14:32:03 +0100486 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
487 /* adjust i so that indices in binds and pfd always match */
488 while (binds[i].sock != pfd[j].fd) {
489 ++i;
490 }
491
492 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200493 --ret;
494
495 if (!ret) {
496 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100497 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200498 break;
499 } else {
500 /* just remember the event for next time */
501 binds[i].pollin = 1;
502 }
503 }
Michal Vasko086311b2016-01-08 09:53:11 +0100504 }
505 }
506 free(pfd);
507
508 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100509 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100510 return -1;
511 }
512
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200513 /* accept connection */
514 client_sock = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
515 if (client_sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200516 ERR(NULL, "Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100517 return -1;
518 }
519
Michal Vasko0190bc32016-03-02 15:47:49 +0100520 /* make the socket non-blocking */
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200521 if (((flags = fcntl(client_sock, F_GETFL)) == -1) || (fcntl(client_sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200522 ERR(NULL, "Fcntl failed (%s).", strerror(errno));
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200523 goto fail;
Michal Vasko0190bc32016-03-02 15:47:49 +0100524 }
525
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200526 /* learn information about the client end */
527 if (saddr.ss_family == AF_UNIX) {
528 if (sock_host_unix(client_sock, &client_address)) {
529 goto fail;
530 }
531 client_port = 0;
532 } else if (saddr.ss_family == AF_INET) {
533 if (sock_host_inet((struct sockaddr_in *)&saddr, &client_address, &client_port)) {
534 goto fail;
535 }
536 } else if (saddr.ss_family == AF_INET6) {
537 if (sock_host_inet6((struct sockaddr_in6 *)&saddr, &client_address, &client_port)) {
538 goto fail;
539 }
540 } else {
541 ERR(NULL, "Source host of an unknown protocol family.");
542 goto fail;
aPiecek90ff0242021-02-14 14:58:01 +0100543 }
Michal Vasko086311b2016-01-08 09:53:11 +0100544
aPiecek90ff0242021-02-14 14:58:01 +0100545 if (saddr.ss_family == AF_UNIX) {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200546 VRB(NULL, "Accepted a connection on %s.", binds[i].address);
aPiecek90ff0242021-02-14 14:58:01 +0100547 } else {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200548 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 +0100549 }
550
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200551 if (host) {
552 *host = client_address;
553 } else {
554 free(client_address);
555 }
556 if (port) {
557 *port = client_port;
558 }
559 if (idx) {
560 *idx = i;
561 }
562 return client_sock;
563
564fail:
565 close(client_sock);
566 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100567}
568
Michal Vasko238b6c12021-12-14 15:14:09 +0100569API struct nc_server_reply *
Michal Vasko05532772021-06-03 12:12:38 +0200570nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100571{
Michal Vasko77367452021-02-16 16:32:18 +0100572 const char *identifier = NULL, *revision = NULL, *format = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100573 char *model_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100574 struct ly_out *out;
Michal Vasko9b1a9522021-03-15 16:24:26 +0100575 const struct lys_module *module = NULL, *mod;
Michal Vasko77367452021-02-16 16:32:18 +0100576 const struct lysp_submodule *submodule = NULL;
577 struct lyd_node *child, *err, *data = NULL;
578 LYS_OUTFORMAT outformat = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100579
Michal Vasko77367452021-02-16 16:32:18 +0100580 LY_LIST_FOR(lyd_child(rpc), child) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100581 if (!strcmp(child->schema->name, "identifier")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200582 identifier = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100583 } else if (!strcmp(child->schema->name, "version")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200584 revision = lyd_get_value(child);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200585 if (revision && (revision[0] == '\0')) {
Michal Vasko77367452021-02-16 16:32:18 +0100586 revision = NULL;
Radek Krejci1afa7792017-03-26 11:24:16 -0500587 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100588 } else if (!strcmp(child->schema->name, "format")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200589 format = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100590 }
591 }
Michal Vasko05532772021-06-03 12:12:38 +0200592 VRB(session, "Schema \"%s@%s\" was requested.", identifier, revision ? revision : "<any>");
Michal Vasko05ba9df2016-01-13 14:40:27 +0100593
Michal Vasko77367452021-02-16 16:32:18 +0100594 /* check revision */
595 if (revision && (strlen(revision) != 10) && strcmp(revision, "1.0")) {
Michal Vasko93224072021-11-09 12:14:28 +0100596 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100597 nc_err_set_msg(err, "The requested version is not supported.", "en");
598 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100599 }
600
Michal Vasko77367452021-02-16 16:32:18 +0100601 if (revision) {
602 /* get specific module */
Michal Vasko93224072021-11-09 12:14:28 +0100603 module = ly_ctx_get_module(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100604 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100605 submodule = ly_ctx_get_submodule(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100606 }
607 } else {
608 /* try to get implemented, then latest module */
Michal Vasko93224072021-11-09 12:14:28 +0100609 module = ly_ctx_get_module_implemented(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100610 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100611 module = ly_ctx_get_module_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100612 }
613 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100614 submodule = ly_ctx_get_submodule_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100615 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200616 }
Michal Vasko77367452021-02-16 16:32:18 +0100617 if (!module && !submodule) {
Michal Vasko93224072021-11-09 12:14:28 +0100618 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100619 nc_err_set_msg(err, "The requested schema was not found.", "en");
620 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100621 }
622
623 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100624 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko77367452021-02-16 16:32:18 +0100625 outformat = LYS_OUT_YANG;
Radek Krejci90fba642016-12-07 15:59:45 +0100626 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko77367452021-02-16 16:32:18 +0100627 outformat = LYS_OUT_YIN;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100628 } else {
Michal Vasko93224072021-11-09 12:14:28 +0100629 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100630 nc_err_set_msg(err, "The requested format is not supported.", "en");
631 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100632 }
Michal Vasko77367452021-02-16 16:32:18 +0100633
634 /* print */
635 ly_out_new_memory(&model_data, 0, &out);
636 if (module) {
637 lys_print_module(out, module, outformat, 0, 0);
638 } else {
639 lys_print_submodule(out, submodule, outformat, 0, 0);
640 }
641 ly_out_free(out, NULL, 0);
Michal Vaskod91f6e62016-04-05 11:34:22 +0200642 if (!model_data) {
643 ERRINT;
644 return NULL;
645 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100646
Michal Vasko9b1a9522021-03-15 16:24:26 +0100647 /* create reply */
Michal Vasko93224072021-11-09 12:14:28 +0100648 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vasko9b1a9522021-03-15 16:24:26 +0100649 if (!mod || lyd_new_inner(NULL, mod, "get-schema", 0, &data)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100650 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200651 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100652 return NULL;
653 }
Michal Vasko9b1a9522021-03-15 16:24:26 +0100654 if (lyd_new_any(data, NULL, "data", model_data, 1, LYD_ANYDATA_STRING, 1, NULL)) {
655 ERRINT;
Michal Vaskoa50f68e2022-02-24 16:10:54 +0100656 free(model_data);
Michal Vasko9b1a9522021-03-15 16:24:26 +0100657 lyd_free_tree(data);
658 return NULL;
659 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100660
Radek Krejci36dfdb32016-09-01 16:56:35 +0200661 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100662}
663
Michal Vasko238b6c12021-12-14 15:14:09 +0100664API struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100665nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100666{
Michal Vasko428087d2016-01-14 16:04:28 +0100667 session->term_reason = NC_SESSION_TERM_CLOSED;
668 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100669}
670
Michal Vasko93224072021-11-09 12:14:28 +0100671/**
672 * @brief Initialize a context with default RPC callbacks if none are set.
673 *
674 * @param[in] ctx Context to initialize.
675 */
676static void
677nc_server_init_ctx(const struct ly_ctx *ctx)
Michal Vasko086311b2016-01-08 09:53:11 +0100678{
Michal Vasko77367452021-02-16 16:32:18 +0100679 struct lysc_node *rpc;
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100680
Michal Vasko238b6c12021-12-14 15:14:09 +0100681 if (global_rpc_clb) {
682 /* expect it to handle these RPCs as well */
683 return;
684 }
685
Michal Vasko05ba9df2016-01-13 14:40:27 +0100686 /* set default <get-schema> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100687 rpc = NULL;
688 if (ly_ctx_get_module_implemented(ctx, "ietf-netconf-monitoring")) {
689 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
690 }
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_get_schema;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100693 }
694
Michal Vasko93224072021-11-09 12:14:28 +0100695 /* set default <close-session> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100696 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf:close-session", 0);
Michal Vasko88639e92017-08-03 14:38:10 +0200697 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100698 rpc->priv = nc_clb_default_close_session;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100699 }
Michal Vasko93224072021-11-09 12:14:28 +0100700}
Michal Vasko05ba9df2016-01-13 14:40:27 +0100701
Michal Vasko93224072021-11-09 12:14:28 +0100702API int
703nc_server_init(void)
704{
705 pthread_rwlockattr_t attr, *attr_p = NULL;
706 int r;
707
708 nc_init();
Michal Vaskob48aa812016-01-18 14:13:09 +0100709
710 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500711 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100712
Michal Vasko93224072021-11-09 12:14:28 +0100713#ifdef HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
714 if ((r = pthread_rwlockattr_init(&attr))) {
715 ERR(NULL, "%s: failed init attribute (%s).", __func__, strerror(r));
716 goto error;
717 }
718 attr_p = &attr;
719 if ((r = pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))) {
720 ERR(NULL, "%s: failed set attribute (%s).", __func__, strerror(r));
721 goto error;
722 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700723#endif
Michal Vasko93224072021-11-09 12:14:28 +0100724
725 if ((r = pthread_rwlock_init(&server_opts.endpt_lock, attr_p))) {
726 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
727 goto error;
728 }
729 if ((r = pthread_rwlock_init(&server_opts.ch_client_lock, attr_p))) {
730 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
731 goto error;
732 }
733
734 if (attr_p) {
735 pthread_rwlockattr_destroy(attr_p);
Frank Rimpler9f838b02018-07-25 06:44:03 +0000736 }
Michal Vasko086311b2016-01-08 09:53:11 +0100737 return 0;
Michal Vasko93224072021-11-09 12:14:28 +0100738
739error:
740 if (attr_p) {
741 pthread_rwlockattr_destroy(attr_p);
742 }
743 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100744}
745
Michal Vaskob48aa812016-01-18 14:13:09 +0100746API void
747nc_server_destroy(void)
748{
Michal Vasko1440a742021-03-31 11:11:03 +0200749 uint32_t i;
Radek Krejci658782b2016-12-04 22:04:55 +0100750
751 for (i = 0; i < server_opts.capabilities_count; i++) {
Michal Vasko93224072021-11-09 12:14:28 +0100752 free(server_opts.capabilities[i]);
Radek Krejci658782b2016-12-04 22:04:55 +0100753 }
754 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200755 server_opts.capabilities = NULL;
756 server_opts.capabilities_count = 0;
Michal Vasko1440a742021-03-31 11:11:03 +0200757 if (server_opts.content_id_data && server_opts.content_id_data_free) {
758 server_opts.content_id_data_free(server_opts.content_id_data);
759 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200760
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200761#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100762 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200763 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100764#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100765#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100766 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
767 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
768 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200769 server_opts.passwd_auth_data = NULL;
770 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100771
Michal Vasko17dfda92016-12-01 14:06:16 +0100772 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100773
774 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
775 server_opts.hostkey_data_free(server_opts.hostkey_data);
776 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200777 server_opts.hostkey_data = NULL;
778 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100779#endif
780#ifdef NC_ENABLED_TLS
781 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
782 server_opts.server_cert_data_free(server_opts.server_cert_data);
783 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200784 server_opts.server_cert_data = NULL;
785 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100786 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
787 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
788 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200789 server_opts.trusted_cert_list_data = NULL;
790 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100791#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100792 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100793}
794
Michal Vasko086311b2016-01-08 09:53:11 +0100795API int
796nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
797{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200798 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
799 ERRARG("basic_mode");
800 return -1;
801 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
802 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100803 return -1;
804 }
805
806 server_opts.wd_basic_mode = basic_mode;
807 server_opts.wd_also_supported = also_supported;
808 return 0;
809}
810
Michal Vasko1a38c862016-01-15 15:50:07 +0100811API void
Michal Vasko55f03972016-04-13 08:56:01 +0200812nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
813{
814 if (!basic_mode && !also_supported) {
815 ERRARG("basic_mode and also_supported");
816 return;
817 }
818
819 if (basic_mode) {
820 *basic_mode = server_opts.wd_basic_mode;
821 }
822 if (also_supported) {
823 *also_supported = server_opts.wd_also_supported;
824 }
825}
826
Michal Vasko55f03972016-04-13 08:56:01 +0200827API int
Radek Krejci658782b2016-12-04 22:04:55 +0100828nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200829{
Michal Vasko93224072021-11-09 12:14:28 +0100830 void *mem;
Radek Krejci658782b2016-12-04 22:04:55 +0100831
832 if (!value || !value[0]) {
833 ERRARG("value must not be empty");
834 return EXIT_FAILURE;
835 }
836
Michal Vasko93224072021-11-09 12:14:28 +0100837 mem = realloc(server_opts.capabilities, (server_opts.capabilities_count + 1) * sizeof *server_opts.capabilities);
838 if (!mem) {
Radek Krejci658782b2016-12-04 22:04:55 +0100839 ERRMEM;
840 return EXIT_FAILURE;
841 }
Michal Vasko93224072021-11-09 12:14:28 +0100842 server_opts.capabilities = mem;
843
844 server_opts.capabilities[server_opts.capabilities_count] = strdup(value);
845 server_opts.capabilities_count++;
Radek Krejci658782b2016-12-04 22:04:55 +0100846
847 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200848}
849
Michal Vasko1a38c862016-01-15 15:50:07 +0100850API void
Michal Vasko1440a742021-03-31 11:11:03 +0200851nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
852 void (*free_user_data)(void *user_data))
853{
854 server_opts.content_id_clb = content_id_clb;
855 server_opts.content_id_data = user_data;
856 server_opts.content_id_data_free = free_user_data;
857}
858
859API void
Michal Vasko086311b2016-01-08 09:53:11 +0100860nc_server_set_hello_timeout(uint16_t hello_timeout)
861{
Michal Vasko086311b2016-01-08 09:53:11 +0100862 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100863}
864
Michal Vasko55f03972016-04-13 08:56:01 +0200865API uint16_t
866nc_server_get_hello_timeout(void)
867{
868 return server_opts.hello_timeout;
869}
870
Michal Vasko1a38c862016-01-15 15:50:07 +0100871API void
Michal Vasko086311b2016-01-08 09:53:11 +0100872nc_server_set_idle_timeout(uint16_t idle_timeout)
873{
Michal Vasko086311b2016-01-08 09:53:11 +0100874 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100875}
876
Michal Vasko55f03972016-04-13 08:56:01 +0200877API uint16_t
878nc_server_get_idle_timeout(void)
879{
880 return server_opts.idle_timeout;
881}
882
Michal Vasko71090fc2016-05-24 16:37:28 +0200883API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +0100884nc_accept_inout(int fdin, int fdout, const char *username, const struct ly_ctx *ctx, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100885{
Michal Vasko71090fc2016-05-24 16:37:28 +0200886 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200887 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200888
Michal Vasko93224072021-11-09 12:14:28 +0100889 if (!ctx) {
890 ERRARG("ctx");
Michal Vasko71090fc2016-05-24 16:37:28 +0200891 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200892 } else if (fdin < 0) {
893 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200894 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200895 } else if (fdout < 0) {
896 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200897 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200898 } else if (!username) {
899 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200900 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200901 } else if (!session) {
902 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200903 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100904 }
905
Michal Vasko93224072021-11-09 12:14:28 +0100906 /* init ctx as needed */
907 nc_server_init_ctx(ctx);
908
Michal Vasko086311b2016-01-08 09:53:11 +0100909 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200910 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100911 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100912 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200913 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100914 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100915 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100916
Michal Vasko086311b2016-01-08 09:53:11 +0100917 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100918 (*session)->ti_type = NC_TI_FD;
919 (*session)->ti.fd.in = fdin;
920 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100921
Michal Vasko93224072021-11-09 12:14:28 +0100922 /* assign context */
Michal Vasko1a38c862016-01-15 15:50:07 +0100923 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +0100924 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100925
Michal Vaskob48aa812016-01-18 14:13:09 +0100926 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200927 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100928
Michal Vasko086311b2016-01-08 09:53:11 +0100929 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200930 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200931 if (msgtype != NC_MSG_HELLO) {
932 nc_session_free(*session, NULL);
933 *session = NULL;
934 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100935 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200936
roman6ece9c52022-06-22 09:29:17 +0200937 nc_gettimespec_mono_add(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +0200938 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
roman6ece9c52022-06-22 09:29:17 +0200939 nc_gettimespec_real_add(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +0200940 (*session)->opts.server.session_start = ts_cur.tv_sec;
941
Michal Vasko1a38c862016-01-15 15:50:07 +0100942 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100943
Michal Vasko71090fc2016-05-24 16:37:28 +0200944 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100945}
Michal Vasko9e036d52016-01-08 10:49:26 +0100946
Michal Vaskob30b99c2016-07-26 11:35:43 +0200947static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100948nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
949{
950 uint8_t q_last;
951
952 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
953 ERRINT;
954 return;
955 }
956
957 /* get a unique queue value (by adding 1 to the last added value, if any) */
958 if (ps->queue_len) {
959 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
960 *id = ps->queue[q_last] + 1;
961 } else {
962 *id = 0;
963 }
964
965 /* add the id into the queue */
966 ++ps->queue_len;
967 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
968 ps->queue[q_last] = *id;
969}
970
971static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200972nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
973{
Michal Vasko74c345f2018-02-07 10:37:11 +0100974 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200975
976 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100977 /* get the actual queue idx */
978 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200979
980 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100981 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200982 /* another equal value, simply cannot be */
983 ERRINT;
984 }
Michal Vaskod8340032018-02-12 14:41:00 +0100985 if (found == 2) {
986 /* move the following values */
987 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
988 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100989 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200990 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100991 if (i == 0) {
992 found = 1;
993 } else {
994 /* this is not okay, our id is in the middle of the queue */
995 found = 2;
996 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200997 }
998 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200999 if (!found) {
1000 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +01001001 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001002 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001003
Michal Vasko103fe632018-02-12 16:37:45 +01001004 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +01001005 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +01001006 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +01001007 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
1008 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001009}
1010
Michal Vaskof04a52a2016-04-07 10:52:10 +02001011int
Michal Vasko26043172016-07-26 14:08:59 +02001012nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001013{
1014 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001015 struct timespec ts;
1016
roman6ece9c52022-06-22 09:29:17 +02001017 nc_gettimespec_real_add(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001018
1019 /* LOCK */
1020 ret = pthread_mutex_timedlock(&ps->lock, &ts);
1021 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001022 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001023 return -1;
1024 }
1025
Michal Vasko74c345f2018-02-07 10:37:11 +01001026 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +01001027 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko05532772021-06-03 12:12:38 +02001028 ERR(NULL, "%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +01001029 pthread_mutex_unlock(&ps->lock);
1030 return -1;
1031 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001032
1033 /* add ourselves into the queue */
1034 nc_ps_queue_add_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001035 DBL(NULL, "PS 0x%p TID %lu queue: added %u, head %u, length %u", ps, (long unsigned int)pthread_self(), *id,
Michal Vasko91290952019-09-27 11:30:55 +02001036 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001037
1038 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001039 while (ps->queue[ps->queue_begin] != *id) {
roman6ece9c52022-06-22 09:29:17 +02001040 nc_gettimespec_real_add(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001041
1042 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
1043 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +05301044 /**
1045 * This may happen when another thread releases the lock and broadcasts the condition
1046 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
1047 * but when actually this thread was ready for condition.
1048 */
preetbhansali629dfc42018-12-17 16:04:40 +05301049 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +05301050 break;
1051 }
Michal Vasko66032bc2019-01-22 15:03:12 +01001052
Michal Vasko05532772021-06-03 12:12:38 +02001053 ERR(NULL, "%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001054 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001055 nc_ps_queue_remove_id(ps, *id);
1056 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001057 return -1;
1058 }
1059 }
1060
Michal Vaskobe86fe32016-04-07 10:43:03 +02001061 /* UNLOCK */
1062 pthread_mutex_unlock(&ps->lock);
1063
1064 return 0;
1065}
1066
Michal Vaskof04a52a2016-04-07 10:52:10 +02001067int
Michal Vasko26043172016-07-26 14:08:59 +02001068nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001069{
1070 int ret;
1071 struct timespec ts;
1072
roman6ece9c52022-06-22 09:29:17 +02001073 nc_gettimespec_real_add(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001074
1075 /* LOCK */
1076 ret = pthread_mutex_timedlock(&ps->lock, &ts);
1077 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001078 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001079 ret = -1;
1080 }
1081
Michal Vaskob30b99c2016-07-26 11:35:43 +02001082 /* we must be the first, it was our turn after all, right? */
1083 if (ps->queue[ps->queue_begin] != id) {
1084 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +02001085 /* UNLOCK */
1086 if (!ret) {
1087 pthread_mutex_unlock(&ps->lock);
1088 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001089 return -1;
1090 }
1091
Michal Vaskobe86fe32016-04-07 10:43:03 +02001092 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001093 nc_ps_queue_remove_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001094 DBL(NULL, "PS 0x%p TID %lu queue: removed %u, head %u, length %u", ps, (long unsigned int)pthread_self(), id,
Michal Vasko91290952019-09-27 11:30:55 +02001095 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001096
1097 /* broadcast to all other threads that the queue moved */
1098 pthread_cond_broadcast(&ps->cond);
1099
Michal Vaskobe86fe32016-04-07 10:43:03 +02001100 /* UNLOCK */
1101 if (!ret) {
1102 pthread_mutex_unlock(&ps->lock);
1103 }
1104
1105 return ret;
1106}
1107
Michal Vasko428087d2016-01-14 16:04:28 +01001108API struct nc_pollsession *
1109nc_ps_new(void)
1110{
Michal Vasko48a63ed2016-03-01 09:48:21 +01001111 struct nc_pollsession *ps;
1112
1113 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +01001114 if (!ps) {
1115 ERRMEM;
1116 return NULL;
1117 }
Michal Vaskobe86fe32016-04-07 10:43:03 +02001118 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001119 pthread_mutex_init(&ps->lock, NULL);
1120
1121 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +01001122}
1123
1124API void
1125nc_ps_free(struct nc_pollsession *ps)
1126{
fanchanghu3d4e7212017-08-09 09:42:30 +08001127 uint16_t i;
1128
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001129 if (!ps) {
1130 return;
1131 }
1132
Michal Vaskobe86fe32016-04-07 10:43:03 +02001133 if (ps->queue_len) {
Michal Vasko05532772021-06-03 12:12:38 +02001134 ERR(NULL, "FATAL: Freeing a pollsession structure that is currently being worked with!");
Michal Vaskobe86fe32016-04-07 10:43:03 +02001135 }
1136
fanchanghu3d4e7212017-08-09 09:42:30 +08001137 for (i = 0; i < ps->session_count; i++) {
1138 free(ps->sessions[i]);
1139 }
1140
Michal Vasko428087d2016-01-14 16:04:28 +01001141 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001142 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001143 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001144
Michal Vasko428087d2016-01-14 16:04:28 +01001145 free(ps);
1146}
1147
1148API int
1149nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1150{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001151 uint8_t q_id;
1152
Michal Vasko45e53ae2016-04-07 11:46:03 +02001153 if (!ps) {
1154 ERRARG("ps");
1155 return -1;
1156 } else if (!session) {
1157 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001158 return -1;
1159 }
1160
Michal Vasko48a63ed2016-03-01 09:48:21 +01001161 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001162 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001163 return -1;
1164 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001165
Michal Vasko428087d2016-01-14 16:04:28 +01001166 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001167 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001168 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001169 ERRMEM;
1170 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001171 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001172 return -1;
1173 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001174 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1175 if (!ps->sessions[ps->session_count - 1]) {
1176 ERRMEM;
1177 --ps->session_count;
1178 /* UNLOCK */
1179 nc_ps_unlock(ps, q_id, __func__);
1180 return -1;
1181 }
1182 ps->sessions[ps->session_count - 1]->session = session;
1183 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001184
Michal Vasko48a63ed2016-03-01 09:48:21 +01001185 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001186 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001187}
1188
Michal Vasko48a63ed2016-03-01 09:48:21 +01001189static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001190_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001191{
1192 uint16_t i;
1193
Radek Krejcid5f978f2016-03-03 13:14:45 +01001194 if (index >= 0) {
1195 i = (uint16_t)index;
1196 goto remove;
1197 }
Michal Vasko428087d2016-01-14 16:04:28 +01001198 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001199 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001200remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001201 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001202 if (i <= ps->session_count) {
1203 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001204 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001205 }
1206 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001207 free(ps->sessions);
1208 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001209 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001210 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001211 return 0;
1212 }
1213 }
1214
Michal Vaskof0537d82016-01-29 14:42:38 +01001215 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001216}
1217
Michal Vasko48a63ed2016-03-01 09:48:21 +01001218API int
1219nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1220{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001221 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001222 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001223
Michal Vasko45e53ae2016-04-07 11:46:03 +02001224 if (!ps) {
1225 ERRARG("ps");
1226 return -1;
1227 } else if (!session) {
1228 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001229 return -1;
1230 }
1231
1232 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001233 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001234 return -1;
1235 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001236
Radek Krejcid5f978f2016-03-03 13:14:45 +01001237 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001238
1239 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001240 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001241
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001242 return ret || ret2 ? -1 : 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001243}
1244
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001245API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001246nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001247{
1248 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001249 struct nc_session *ret = NULL;
1250
1251 if (!ps) {
1252 ERRARG("ps");
1253 return NULL;
1254 }
1255
1256 /* LOCK */
1257 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1258 return NULL;
1259 }
1260
Michal Vasko4871c9d2017-10-09 14:48:39 +02001261 if (idx < ps->session_count) {
1262 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001263 }
1264
1265 /* UNLOCK */
1266 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1267
1268 return ret;
1269}
1270
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001271API uint16_t
1272nc_ps_session_count(struct nc_pollsession *ps)
1273{
Michal Vasko47003942019-03-14 12:25:23 +01001274 uint8_t q_id;
1275 uint16_t session_count;
1276
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001277 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001278 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001279 return 0;
1280 }
1281
Michal Vasko47003942019-03-14 12:25:23 +01001282 /* LOCK (just for memory barrier so that we read the current value) */
1283 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1284 return 0;
1285 }
1286
1287 session_count = ps->session_count;
1288
1289 /* UNLOCK */
1290 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1291
1292 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001293}
1294
Michal Vasko131120a2018-05-29 15:44:02 +02001295/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001296 * returns: NC_PSPOLL_ERROR,
Michal Vasko77367452021-02-16 16:32:18 +01001297 * NC_PSPOLL_TIMEOUT,
Michal Vasko71090fc2016-05-24 16:37:28 +02001298 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1299 * NC_PSPOLL_RPC
1300 */
1301static int
Michal Vasko131120a2018-05-29 15:44:02 +02001302nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001303{
Michal Vasko77367452021-02-16 16:32:18 +01001304 struct ly_in *msg;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001305 struct nc_server_reply *reply = NULL;
Michal Vasko939ffce2021-04-12 13:02:01 +02001306 struct lyd_node *e;
Michal Vasko77367452021-02-16 16:32:18 +01001307 int r, ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001308
Michal Vasko45e53ae2016-04-07 11:46:03 +02001309 if (!session) {
1310 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001311 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001312 } else if (!rpc) {
1313 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001314 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001315 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vasko05532772021-06-03 12:12:38 +02001316 ERR(session, "Invalid session to receive RPCs.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001317 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001318 }
1319
Michal Vasko93224072021-11-09 12:14:28 +01001320 *rpc = NULL;
1321
Michal Vasko77367452021-02-16 16:32:18 +01001322 /* get a message */
1323 r = nc_read_msg_io(session, io_timeout, &msg, 0);
1324 if (r == -2) {
1325 /* malformed message */
1326 ret = NC_PSPOLL_REPLY_ERROR;
Michal Vasko93224072021-11-09 12:14:28 +01001327 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vasko77367452021-02-16 16:32:18 +01001328 goto send_reply;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001329 }
1330 if (r == -1) {
Michal Vasko77367452021-02-16 16:32:18 +01001331 return NC_PSPOLL_ERROR;
1332 } else if (!r) {
1333 return NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001334 }
1335
Michal Vasko77367452021-02-16 16:32:18 +01001336 *rpc = calloc(1, sizeof **rpc);
1337 if (!*rpc) {
1338 ERRMEM;
1339 ret = NC_PSPOLL_REPLY_ERROR;
1340 goto cleanup;
1341 }
1342
1343 /* parse the RPC */
Michal Vasko93224072021-11-09 12:14:28 +01001344 if (lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_RPC_NETCONF, &(*rpc)->envp, &(*rpc)->rpc)) {
Michal Vasko77367452021-02-16 16:32:18 +01001345 /* bad RPC received */
1346 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1347
1348 if ((*rpc)->envp) {
1349 /* at least the envelopes were parsed */
Michal Vasko93224072021-11-09 12:14:28 +01001350 e = nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
1351 nc_err_set_msg(e, ly_errmsg(session->ctx), "en");
Michal Vasko939ffce2021-04-12 13:02:01 +02001352 reply = nc_server_reply_err(e);
Michal Vasko77367452021-02-16 16:32:18 +01001353 } else if (session->version == NC_VERSION_11) {
Michal Vasko93224072021-11-09 12:14:28 +01001354 /* completely malformed message, NETCONF version 1.1 defines sending error reply from
1355 * the server (RFC 6241 sec. 3) */
1356 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vasko77367452021-02-16 16:32:18 +01001357 }
1358
1359send_reply:
1360 if (reply) {
Michal Vasko93224072021-11-09 12:14:28 +01001361 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, *rpc ? (*rpc)->envp : NULL, reply);
Michal Vasko77367452021-02-16 16:32:18 +01001362 nc_server_reply_free(reply);
1363 if (r != NC_MSG_REPLY) {
Michal Vasko05532772021-06-03 12:12:38 +02001364 ERR(session, "Failed to write reply (%s), terminating session.", nc_msgtype2str[r]);
Michal Vasko77367452021-02-16 16:32:18 +01001365 if (session->status != NC_STATUS_INVALID) {
1366 session->status = NC_STATUS_INVALID;
1367 session->term_reason = NC_SESSION_TERM_OTHER;
1368 }
1369 }
1370 }
1371 } else {
1372 ret = NC_PSPOLL_RPC;
1373 }
1374
1375cleanup:
1376 ly_in_free(msg, 1);
1377 if (ret != NC_PSPOLL_RPC) {
1378 nc_server_rpc_free(*rpc);
1379 *rpc = NULL;
1380 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001381 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001382}
1383
fanchanghu966f2de2016-07-21 02:28:57 -04001384API void
1385nc_set_global_rpc_clb(nc_rpc_clb clb)
1386{
1387 global_rpc_clb = clb;
1388}
1389
Radek Krejci93e80222016-10-03 13:34:25 +02001390API NC_MSG_TYPE
1391nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1392{
Michal Vasko131120a2018-05-29 15:44:02 +02001393 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001394
1395 /* check parameters */
Michal Vaskodf68e7e2022-04-21 11:04:00 +02001396 if (!session || (session->side != NC_SERVER) || !nc_session_get_notif_status(session)) {
Radek Krejci93e80222016-10-03 13:34:25 +02001397 ERRARG("session");
1398 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001399 } else if (!notif || !notif->ntf || !notif->eventtime) {
Radek Krejci93e80222016-10-03 13:34:25 +02001400 ERRARG("notif");
1401 return NC_MSG_ERROR;
1402 }
1403
Michal Vasko131120a2018-05-29 15:44:02 +02001404 /* we do not need RPC lock for this, IO lock will be acquired properly */
1405 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001406 if (ret != NC_MSG_NOTIF) {
Michal Vasko05532772021-06-03 12:12:38 +02001407 ERR(session, "Failed to write notification (%s).", nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001408 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001409
Michal Vasko131120a2018-05-29 15:44:02 +02001410 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001411}
1412
Michal Vasko131120a2018-05-29 15:44:02 +02001413/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001414 * returns: NC_PSPOLL_ERROR,
1415 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1416 * NC_PSPOLL_REPLY_ERROR,
1417 * 0
1418 */
1419static int
Michal Vasko93224072021-11-09 12:14:28 +01001420nc_server_send_reply_io(struct nc_session *session, int io_timeout, const struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001421{
1422 nc_rpc_clb clb;
1423 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +01001424 const struct lysc_node *rpc_act = NULL;
1425 struct lyd_node *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001426 int ret = 0;
1427 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001428
Michal Vasko4a827e52016-03-03 10:59:00 +01001429 if (!rpc) {
1430 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001431 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001432 }
1433
Michal Vasko77367452021-02-16 16:32:18 +01001434 if (rpc->rpc->schema->nodetype == LYS_RPC) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001435 /* RPC */
Michal Vasko77367452021-02-16 16:32:18 +01001436 rpc_act = rpc->rpc->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001437 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001438 /* action */
Michal Vasko77367452021-02-16 16:32:18 +01001439 LYD_TREE_DFS_BEGIN(rpc->rpc, elem) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001440 if (elem->schema->nodetype == LYS_ACTION) {
1441 rpc_act = elem->schema;
1442 break;
1443 }
Michal Vasko77367452021-02-16 16:32:18 +01001444 LYD_TREE_DFS_END(rpc->rpc, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001445 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001446 if (!rpc_act) {
1447 ERRINT;
1448 return NC_PSPOLL_ERROR;
1449 }
1450 }
1451
1452 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001453 if (!global_rpc_clb) {
1454 /* no callback, reply with a not-implemented error */
Michal Vasko93224072021-11-09 12:14:28 +01001455 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001456 } else {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001457 reply = global_rpc_clb(rpc->rpc, session);
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001458 }
Michal Vasko428087d2016-01-14 16:04:28 +01001459 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001460 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko77367452021-02-16 16:32:18 +01001461 reply = clb(rpc->rpc, session);
Michal Vasko428087d2016-01-14 16:04:28 +01001462 }
1463
1464 if (!reply) {
Michal Vasko93224072021-11-09 12:14:28 +01001465 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001466 }
Michal Vasko77367452021-02-16 16:32:18 +01001467 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->envp, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001468 if (reply->type == NC_RPL_ERROR) {
1469 ret |= NC_PSPOLL_REPLY_ERROR;
1470 }
1471 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001472
Michal Vasko131120a2018-05-29 15:44:02 +02001473 if (r != NC_MSG_REPLY) {
Michal Vasko15469492021-06-09 08:40:48 +02001474 ERR(session, "Failed to write reply (%s).", nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001475 ret |= NC_PSPOLL_ERROR;
1476 }
Michal Vasko428087d2016-01-14 16:04:28 +01001477
1478 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1479 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1480 session->status = NC_STATUS_INVALID;
1481 }
1482
Michal Vasko71090fc2016-05-24 16:37:28 +02001483 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001484}
1485
Michal Vasko131120a2018-05-29 15:44:02 +02001486/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001487 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1488 * NC_PSPOLL_ERROR, (msg filled)
1489 * NC_PSPOLL_TIMEOUT,
1490 * NC_PSPOLL_RPC (some application data available),
1491 * NC_PSPOLL_SSH_CHANNEL,
1492 * NC_PSPOLL_SSH_MSG
1493 */
1494static int
Michal Vasko131120a2018-05-29 15:44:02 +02001495nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001496{
Michal Vasko9a327362017-01-11 11:31:46 +01001497 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001498 int r, ret = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001499
Michal Vasko9a327362017-01-11 11:31:46 +01001500#ifdef NC_ENABLED_SSH
1501 struct nc_session *new;
1502#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001503
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001504 /* check timeout first */
Michal Vaskodf68e7e2022-04-21 11:04:00 +02001505 if (!(session->flags & NC_SESSION_CALLHOME) && !nc_session_get_notif_status(session) && server_opts.idle_timeout &&
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001506 (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001507 sprintf(msg, "session idle timeout elapsed");
1508 session->status = NC_STATUS_INVALID;
1509 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1510 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1511 }
1512
Michal Vasko131120a2018-05-29 15:44:02 +02001513 r = nc_session_io_lock(session, io_timeout, __func__);
1514 if (r < 0) {
1515 sprintf(msg, "session IO lock failed to be acquired");
1516 return NC_PSPOLL_ERROR;
1517 } else if (!r) {
1518 return NC_PSPOLL_TIMEOUT;
1519 }
1520
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001521 switch (session->ti_type) {
1522#ifdef NC_ENABLED_SSH
1523 case NC_TI_LIBSSH:
1524 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001525 if (r == SSH_EOF) {
1526 sprintf(msg, "SSH channel unexpected EOF");
1527 session->status = NC_STATUS_INVALID;
1528 session->term_reason = NC_SESSION_TERM_DROPPED;
1529 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1530 } else if (r == SSH_ERROR) {
1531 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001532 session->status = NC_STATUS_INVALID;
1533 session->term_reason = NC_SESSION_TERM_OTHER;
1534 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001535 } else if (!r) {
1536 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1537 /* new SSH message */
1538 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1539 if (session->ti.libssh.next) {
1540 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001541 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel &&
1542 (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko8dcaa882017-10-19 14:28:42 +02001543 /* new NETCONF SSH channel */
1544 ret = NC_PSPOLL_SSH_CHANNEL;
1545 break;
1546 }
1547 }
1548 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001549 break;
1550 }
1551 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001552
Michal Vasko8dcaa882017-10-19 14:28:42 +02001553 /* just some SSH message */
1554 ret = NC_PSPOLL_SSH_MSG;
1555 } else {
1556 ret = NC_PSPOLL_TIMEOUT;
1557 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001558 } else {
1559 /* we have some application data */
1560 ret = NC_PSPOLL_RPC;
1561 }
1562 break;
1563#endif
1564#ifdef NC_ENABLED_TLS
1565 case NC_TI_OPENSSL:
1566 r = SSL_pending(session->ti.tls);
1567 if (!r) {
1568 /* no data pending in the SSL buffer, poll fd */
1569 pfd.fd = SSL_get_rfd(session->ti.tls);
1570 if (pfd.fd < 0) {
1571 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1572 ret = NC_PSPOLL_ERROR;
1573 break;
1574 }
1575 pfd.events = POLLIN;
1576 pfd.revents = 0;
1577 r = poll(&pfd, 1, 0);
1578
1579 if ((r < 0) && (errno != EINTR)) {
1580 sprintf(msg, "poll failed (%s)", strerror(errno));
1581 session->status = NC_STATUS_INVALID;
1582 ret = NC_PSPOLL_ERROR;
1583 } else if (r > 0) {
1584 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1585 sprintf(msg, "communication socket unexpectedly closed");
1586 session->status = NC_STATUS_INVALID;
1587 session->term_reason = NC_SESSION_TERM_DROPPED;
1588 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1589 } else if (pfd.revents & POLLERR) {
1590 sprintf(msg, "communication socket error");
1591 session->status = NC_STATUS_INVALID;
1592 session->term_reason = NC_SESSION_TERM_OTHER;
1593 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1594 } else {
1595 ret = NC_PSPOLL_RPC;
1596 }
1597 } else {
1598 ret = NC_PSPOLL_TIMEOUT;
1599 }
1600 } else {
1601 ret = NC_PSPOLL_RPC;
1602 }
1603 break;
1604#endif
1605 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001606 case NC_TI_UNIX:
1607 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001608 pfd.events = POLLIN;
1609 pfd.revents = 0;
1610 r = poll(&pfd, 1, 0);
1611
1612 if ((r < 0) && (errno != EINTR)) {
1613 sprintf(msg, "poll failed (%s)", strerror(errno));
1614 session->status = NC_STATUS_INVALID;
1615 ret = NC_PSPOLL_ERROR;
1616 } else if (r > 0) {
1617 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1618 sprintf(msg, "communication socket unexpectedly closed");
1619 session->status = NC_STATUS_INVALID;
1620 session->term_reason = NC_SESSION_TERM_DROPPED;
1621 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1622 } else if (pfd.revents & POLLERR) {
1623 sprintf(msg, "communication socket error");
1624 session->status = NC_STATUS_INVALID;
1625 session->term_reason = NC_SESSION_TERM_OTHER;
1626 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1627 } else {
1628 ret = NC_PSPOLL_RPC;
1629 }
1630 } else {
1631 ret = NC_PSPOLL_TIMEOUT;
1632 }
1633 break;
1634 case NC_TI_NONE:
1635 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1636 ret = NC_PSPOLL_ERROR;
1637 break;
1638 }
1639
Michal Vasko131120a2018-05-29 15:44:02 +02001640 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001641 return ret;
1642}
1643
1644API int
1645nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1646{
1647 int ret, r;
1648 uint8_t q_id;
1649 uint16_t i, j;
1650 char msg[256];
1651 struct timespec ts_timeout, ts_cur;
1652 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001653 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001654 struct nc_server_rpc *rpc = NULL;
1655
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001656 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001657 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001658 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001659 }
1660
Michal Vaskoade892d2017-02-22 13:40:35 +01001661 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001662 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001663 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001664 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001665
Michal Vaskoade892d2017-02-22 13:40:35 +01001666 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001667 nc_ps_unlock(ps, q_id, __func__);
1668 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001669 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001670
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001671 /* fill timespecs */
roman6ece9c52022-06-22 09:29:17 +02001672 nc_gettimespec_mono_add(&ts_cur, 0);
Michal Vasko36c7be82017-02-22 13:37:59 +01001673 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001674 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001675 }
1676
1677 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001678 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001679 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001680 if (ps->last_event_session == ps->session_count - 1) {
1681 i = j = 0;
1682 } else {
1683 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001684 }
Michal Vasko9a327362017-01-11 11:31:46 +01001685 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001686 cur_ps_session = ps->sessions[i];
1687 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001688
Michal Vasko131120a2018-05-29 15:44:02 +02001689 /* SESSION RPC LOCK */
1690 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001691 if (r == -1) {
1692 ret = NC_PSPOLL_ERROR;
1693 } else if (r == 1) {
1694 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001695 switch (cur_ps_session->state) {
1696 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001697 if (cur_session->status == NC_STATUS_RUNNING) {
1698 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001699 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001700
Michal Vasko131120a2018-05-29 15:44:02 +02001701 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001702 switch (ret) {
1703 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001704 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001705 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001706 break;
1707 case NC_PSPOLL_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001708 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001709 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001710 break;
1711 case NC_PSPOLL_TIMEOUT:
1712#ifdef NC_ENABLED_SSH
1713 case NC_PSPOLL_SSH_CHANNEL:
1714 case NC_PSPOLL_SSH_MSG:
1715#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001716 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001717 break;
1718 case NC_PSPOLL_RPC:
1719 /* let's keep the state busy, we are not done with this session */
1720 break;
1721 }
1722 } else {
1723 /* session is not fine, let the caller know */
1724 ret = NC_PSPOLL_SESSION_TERM;
1725 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1726 ret |= NC_PSPOLL_SESSION_ERROR;
1727 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001728 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001729 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001730 break;
1731 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001732 /* it definitely should not be busy because we have the lock */
1733 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001734 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001735 break;
1736 case NC_PS_STATE_INVALID:
1737 /* we got it locked, but it will be freed, let it be */
1738 ret = NC_PSPOLL_TIMEOUT;
1739 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001740 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001741
Michal Vasko131120a2018-05-29 15:44:02 +02001742 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001743 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001744 /* SESSION RPC UNLOCK */
1745 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001746 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001747 } else {
1748 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001749 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001750 }
Michal Vasko428087d2016-01-14 16:04:28 +01001751
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001752 /* something happened */
1753 if (ret != NC_PSPOLL_TIMEOUT) {
1754 break;
1755 }
1756
Michal Vasko9a327362017-01-11 11:31:46 +01001757 if (i == ps->session_count - 1) {
1758 i = 0;
1759 } else {
1760 ++i;
1761 }
1762 } while (i != j);
1763
Michal Vaskoade892d2017-02-22 13:40:35 +01001764 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001765 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001766 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001767
roman6ece9c52022-06-22 09:29:17 +02001768 if ((timeout > -1) && (nc_difftimespec_cur(&ts_timeout) < 1)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001769 /* final timeout */
1770 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001771 }
Michal Vasko428087d2016-01-14 16:04:28 +01001772 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001773 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001774
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001775 /* do we want to return the session? */
1776 switch (ret) {
1777 case NC_PSPOLL_RPC:
1778 case NC_PSPOLL_SESSION_TERM:
1779 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1780#ifdef NC_ENABLED_SSH
1781 case NC_PSPOLL_SSH_CHANNEL:
1782 case NC_PSPOLL_SSH_MSG:
1783#endif
1784 if (session) {
1785 *session = cur_session;
1786 }
1787 ps->last_event_session = i;
1788 break;
1789 default:
1790 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001791 }
Michal Vasko428087d2016-01-14 16:04:28 +01001792
Michal Vaskoade892d2017-02-22 13:40:35 +01001793 /* PS UNLOCK */
1794 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001795
Michal Vasko131120a2018-05-29 15:44:02 +02001796 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001797 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001798 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001799 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1800 if (cur_session->status != NC_STATUS_RUNNING) {
1801 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001802 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001803 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001804 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001805 }
1806 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001807 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001808
Michal Vasko7f1ee932018-10-11 09:41:42 +02001809 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001810 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001811 if (cur_session->status != NC_STATUS_RUNNING) {
1812 ret |= NC_PSPOLL_SESSION_TERM;
1813 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1814 ret |= NC_PSPOLL_SESSION_ERROR;
1815 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001816 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001817 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001818 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001819 }
Michal Vasko428087d2016-01-14 16:04:28 +01001820 }
Michal Vasko77367452021-02-16 16:32:18 +01001821 nc_server_rpc_free(rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001822
Michal Vasko131120a2018-05-29 15:44:02 +02001823 /* SESSION RPC UNLOCK */
1824 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001825 }
1826
Michal Vasko48a63ed2016-03-01 09:48:21 +01001827 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001828}
1829
Michal Vaskod09eae62016-02-01 10:32:52 +01001830API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001831nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001832{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001833 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001834 uint16_t i;
1835 struct nc_session *session;
1836
Michal Vasko9a25e932016-02-01 10:36:42 +01001837 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001838 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001839 return;
1840 }
1841
Michal Vasko48a63ed2016-03-01 09:48:21 +01001842 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001843 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001844 return;
1845 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001846
Michal Vasko48a63ed2016-03-01 09:48:21 +01001847 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001848 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001849 nc_session_free(ps->sessions[i]->session, data_free);
1850 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001851 }
1852 free(ps->sessions);
1853 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001854 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001855 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001856 } else {
1857 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001858 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1859 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001860 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001861 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001862 continue;
1863 }
1864
1865 ++i;
1866 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001867 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001868
1869 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001870 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001871}
1872
Michal Vasko5f352c52019-07-10 16:12:06 +02001873static int
apropp-molex4e903c32020-04-20 03:06:58 -04001874nc_get_uid(int sock, uid_t *uid)
1875{
Michal Vaskod3910912020-04-20 09:12:49 +02001876 int ret;
apropp-molex4e903c32020-04-20 03:06:58 -04001877
Michal Vaskod3910912020-04-20 09:12:49 +02001878#ifdef SO_PEERCRED
1879 struct ucred ucred;
1880 socklen_t len;
1881 len = sizeof(ucred);
1882 ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
1883 if (!ret) {
1884 *uid = ucred.uid;
1885 }
1886#else
1887 ret = getpeereid(sock, uid, NULL);
1888#endif
1889
1890 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001891 ERR(NULL, "Failed to get credentials from unix socket (%s).", strerror(errno));
Michal Vaskod3910912020-04-20 09:12:49 +02001892 return -1;
1893 }
apropp-molex4e903c32020-04-20 03:06:58 -04001894 return 0;
1895}
1896
1897static int
Michal Vasko5f352c52019-07-10 16:12:06 +02001898nc_accept_unix(struct nc_session *session, int sock)
1899{
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001900#if defined (SO_PEERCRED) || defined (HAVE_GETPEEREID)
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001901 struct passwd *pw, pw_buf;
Michal Vasko5f352c52019-07-10 16:12:06 +02001902 char *username;
Michal Vasko5f352c52019-07-10 16:12:06 +02001903 session->ti_type = NC_TI_UNIX;
Michal Vasko143aa142021-10-01 15:31:48 +02001904 uid_t uid = 0;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001905 char *buf = NULL;
1906 size_t buf_len = 0;
Michal Vasko5f352c52019-07-10 16:12:06 +02001907
Michal Vaskod3910912020-04-20 09:12:49 +02001908 if (nc_get_uid(sock, &uid)) {
1909 close(sock);
Michal Vasko5f352c52019-07-10 16:12:06 +02001910 return -1;
1911 }
1912
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001913 pw = nc_getpwuid(uid, &pw_buf, &buf, &buf_len);
Michal Vasko5f352c52019-07-10 16:12:06 +02001914 if (pw == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001915 ERR(NULL, "Failed to find username for uid=%u (%s).\n", uid, strerror(errno));
Michal Vasko5f352c52019-07-10 16:12:06 +02001916 close(sock);
1917 return -1;
1918 }
1919
1920 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001921 free(buf);
Michal Vasko5f352c52019-07-10 16:12:06 +02001922 if (username == NULL) {
1923 ERRMEM;
1924 close(sock);
1925 return -1;
1926 }
Michal Vasko93224072021-11-09 12:14:28 +01001927 session->username = username;
Michal Vasko5f352c52019-07-10 16:12:06 +02001928
1929 session->ti.unixsock.sock = sock;
1930
1931 return 1;
Claus Klein22091912020-01-20 13:45:47 +01001932#else
1933 return -1;
1934#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02001935}
1936
Michal Vaskoe2713da2016-08-22 16:06:40 +02001937API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001938nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001939{
Michal Vasko3031aae2016-01-27 16:07:18 +01001940 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001941 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001942
Michal Vasko45e53ae2016-04-07 11:46:03 +02001943 if (!name) {
1944 ERRARG("name");
1945 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001946 }
1947
Michal Vaskoade892d2017-02-22 13:40:35 +01001948 /* BIND LOCK */
1949 pthread_mutex_lock(&server_opts.bind_lock);
1950
1951 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001952 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001953
1954 /* check name uniqueness */
1955 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001956 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02001957 ERR(NULL, "Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001958 ret = -1;
1959 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001960 }
1961 }
1962
Michal Vasko93224072021-11-09 12:14:28 +01001963 server_opts.endpts = nc_realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001964 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001965 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001966 ret = -1;
1967 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001968 }
Michal Vasko93224072021-11-09 12:14:28 +01001969 memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts);
1970 ++server_opts.endpt_count;
1971
1972 server_opts.endpts[server_opts.endpt_count - 1].name = strdup(name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001973 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001974 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
1975 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
1976 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001977
Michal Vaskoe2713da2016-08-22 16:06:40 +02001978 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001979 if (!server_opts.binds) {
1980 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001981 ret = -1;
1982 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001983 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001984
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001985 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001986 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1987
1988 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001989#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001990 case NC_TI_LIBSSH:
1991 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1992 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1993 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001994 ret = -1;
1995 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001996 }
1997 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
Michal Vasko77367452021-02-16 16:32:18 +01001998 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001999 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002000 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002001 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002002#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02002003#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002004 case NC_TI_OPENSSL:
2005 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2006 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
2007 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002008 ret = -1;
2009 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002010 }
2011 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002012#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002013 case NC_TI_UNIX:
2014 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
2015 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
2016 ERRMEM;
2017 ret = -1;
2018 goto cleanup;
2019 }
2020 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
2021 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
2022 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
2023 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002024 default:
2025 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01002026 ret = -1;
2027 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002028 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002029
Michal Vaskoade892d2017-02-22 13:40:35 +01002030cleanup:
2031 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002032 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002033
Michal Vaskoade892d2017-02-22 13:40:35 +01002034 /* BIND UNLOCK */
2035 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002036
Michal Vaskoade892d2017-02-22 13:40:35 +01002037 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002038}
2039
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002040API int
Michal Vasko59050372016-11-22 14:33:55 +01002041nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01002042{
2043 uint32_t i;
2044 int ret = -1;
2045
Michal Vaskoade892d2017-02-22 13:40:35 +01002046 /* BIND LOCK */
2047 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002048
Michal Vaskoade892d2017-02-22 13:40:35 +01002049 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002050 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002051
Michal Vasko59050372016-11-22 14:33:55 +01002052 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002053 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01002054 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002055 free(server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002056 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002057#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002058 case NC_TI_LIBSSH:
2059 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2060 free(server_opts.endpts[i].opts.ssh);
2061 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002062#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002063#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002064 case NC_TI_OPENSSL:
2065 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2066 free(server_opts.endpts[i].opts.tls);
2067 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002068#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002069 case NC_TI_UNIX:
2070 free(server_opts.endpts[i].opts.unixsock);
2071 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002072 default:
2073 ERRINT;
2074 /* won't get here ...*/
2075 break;
2076 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002077 ret = 0;
2078 }
Michal Vasko3031aae2016-01-27 16:07:18 +01002079 free(server_opts.endpts);
2080 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002081
2082 /* remove all binds */
2083 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002084 free(server_opts.binds[i].address);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002085 if (server_opts.binds[i].sock > -1) {
2086 close(server_opts.binds[i].sock);
2087 }
2088 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002089 free(server_opts.binds);
2090 server_opts.binds = NULL;
2091
Michal Vasko3031aae2016-01-27 16:07:18 +01002092 server_opts.endpt_count = 0;
2093
Michal Vasko1a38c862016-01-15 15:50:07 +01002094 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002095 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002096 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002097 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002098 /* remove endpt */
Michal Vasko93224072021-11-09 12:14:28 +01002099 free(server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002100 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002101#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002102 case NC_TI_LIBSSH:
2103 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2104 free(server_opts.endpts[i].opts.ssh);
2105 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002106#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002107#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002108 case NC_TI_OPENSSL:
2109 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2110 free(server_opts.endpts[i].opts.tls);
2111 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002112#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002113 case NC_TI_UNIX:
2114 free(server_opts.endpts[i].opts.unixsock);
2115 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002116 default:
2117 ERRINT;
2118 break;
2119 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002120
Michal Vaskoe2713da2016-08-22 16:06:40 +02002121 /* remove bind(s) */
Michal Vasko93224072021-11-09 12:14:28 +01002122 free(server_opts.binds[i].address);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002123 if (server_opts.binds[i].sock > -1) {
2124 close(server_opts.binds[i].sock);
2125 }
2126
2127 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002128 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002129 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002130 free(server_opts.binds);
2131 server_opts.binds = NULL;
2132 free(server_opts.endpts);
2133 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002134 } else if (i < server_opts.endpt_count) {
2135 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2136 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002137 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002138
2139 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002140 if (name) {
2141 break;
2142 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002143 }
2144 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002145 }
2146
Michal Vaskoade892d2017-02-22 13:40:35 +01002147 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002148 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002149
Michal Vaskoade892d2017-02-22 13:40:35 +01002150 /* BIND UNLOCK */
2151 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002152
2153 return ret;
2154}
2155
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002156API int
2157nc_server_endpt_count(void)
2158{
2159 return server_opts.endpt_count;
2160}
2161
Michal Vasko1b5973e2020-01-30 16:05:46 +01002162API int
2163nc_server_is_endpt(const char *name)
2164{
2165 uint16_t i;
2166 int found = 0;
2167
Michal Vaskofb1724b2020-01-31 11:02:00 +01002168 if (!name) {
2169 return found;
2170 }
2171
Michal Vasko1b5973e2020-01-30 16:05:46 +01002172 /* ENDPT READ LOCK */
2173 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2174
2175 /* check name uniqueness */
2176 for (i = 0; i < server_opts.endpt_count; ++i) {
2177 if (!strcmp(server_opts.endpts[i].name, name)) {
2178 found = 1;
2179 break;
2180 }
2181 }
2182
2183 /* ENDPT UNLOCK */
2184 pthread_rwlock_unlock(&server_opts.endpt_lock);
2185
2186 return found;
2187}
2188
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002189int
2190nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
2191{
2192 struct nc_endpt *endpt;
2193 struct nc_bind *bind = NULL;
2194 uint16_t i;
2195 int sock = -1, set_addr, ret = 0;
2196
2197 if (!endpt_name) {
2198 ERRARG("endpt_name");
2199 return -1;
2200 } else if ((!address && !port) || (address && port)) {
2201 ERRARG("address and port");
2202 return -1;
2203 }
2204
2205 if (address) {
2206 set_addr = 1;
2207 } else {
2208 set_addr = 0;
2209 }
2210
2211 /* BIND LOCK */
2212 pthread_mutex_lock(&server_opts.bind_lock);
2213
2214 /* ENDPT LOCK */
2215 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2216 if (!endpt) {
2217 /* BIND UNLOCK */
2218 pthread_mutex_unlock(&server_opts.bind_lock);
2219 return -1;
2220 }
2221
2222 bind = &server_opts.binds[i];
2223
2224 if (set_addr) {
2225 port = bind->port;
2226 } else {
2227 address = bind->address;
2228 }
2229
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002230 if (!set_addr && (endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002231 ret = -1;
2232 goto cleanup;
2233 }
2234
2235 /* we have all the information we need to create a listening socket */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002236 if (address && (port || (endpt->ti == NC_TI_UNIX))) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002237 /* create new socket, close the old one */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002238 if (endpt->ti == NC_TI_UNIX) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002239 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002240 } else {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002241 sock = nc_sock_listen_inet(address, port, &endpt->ka);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002242 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002243 if (sock == -1) {
2244 ret = -1;
2245 goto cleanup;
2246 }
2247
2248 if (bind->sock > -1) {
2249 close(bind->sock);
2250 }
2251 bind->sock = sock;
2252 } /* else we are just setting address or port */
2253
2254 if (set_addr) {
Michal Vasko93224072021-11-09 12:14:28 +01002255 free(bind->address);
2256 bind->address = strdup(address);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002257 } else {
2258 bind->port = port;
2259 }
2260
2261 if (sock > -1) {
Michal Vasko946cacb2020-08-12 11:18:08 +02002262 switch (endpt->ti) {
2263 case NC_TI_UNIX:
Michal Vasko05532772021-06-03 12:12:38 +02002264 VRB(NULL, "Listening on %s for UNIX connections.", address);
Michal Vasko946cacb2020-08-12 11:18:08 +02002265 break;
2266#ifdef NC_ENABLED_SSH
2267 case NC_TI_LIBSSH:
Michal Vasko05532772021-06-03 12:12:38 +02002268 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002269 break;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002270#endif
Michal Vasko946cacb2020-08-12 11:18:08 +02002271#ifdef NC_ENABLED_TLS
2272 case NC_TI_OPENSSL:
Michal Vasko05532772021-06-03 12:12:38 +02002273 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002274 break;
2275#endif
2276 default:
2277 ERRINT;
2278 break;
2279 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002280 }
2281
2282cleanup:
2283 /* ENDPT UNLOCK */
2284 pthread_rwlock_unlock(&server_opts.endpt_lock);
2285
2286 /* BIND UNLOCK */
2287 pthread_mutex_unlock(&server_opts.bind_lock);
2288
2289 return ret;
2290}
2291
2292API int
2293nc_server_endpt_set_address(const char *endpt_name, const char *address)
2294{
2295 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2296}
2297
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002298#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002299
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002300API int
2301nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2302{
2303 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2304}
2305
Michal Vasko946cacb2020-08-12 11:18:08 +02002306#endif
2307
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002308API int
2309nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2310{
2311 struct nc_endpt *endpt;
2312 uint16_t i;
2313 int ret = 0;
2314
2315 if (!endpt_name) {
2316 ERRARG("endpt_name");
2317 return -1;
2318 } else if (mode == 0) {
2319 ERRARG("mode");
2320 return -1;
2321 }
2322
2323 /* ENDPT LOCK */
2324 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002325 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002326 return -1;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002327 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002328
2329 if (endpt->ti != NC_TI_UNIX) {
2330 ret = -1;
2331 goto cleanup;
2332 }
2333
2334 endpt->opts.unixsock->mode = mode;
2335 endpt->opts.unixsock->uid = uid;
2336 endpt->opts.unixsock->gid = gid;
2337
2338cleanup:
2339 /* ENDPT UNLOCK */
2340 pthread_rwlock_unlock(&server_opts.endpt_lock);
2341
2342 return ret;
2343}
2344
2345API int
2346nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2347{
2348 struct nc_endpt *endpt;
2349 int ret = 0;
2350
2351 if (!endpt_name) {
2352 ERRARG("endpt_name");
2353 return -1;
2354 }
2355
2356 /* ENDPT LOCK */
2357 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2358 if (!endpt) {
2359 return -1;
2360 }
2361
2362 endpt->ka.enabled = (enable ? 1 : 0);
2363
2364 /* ENDPT UNLOCK */
2365 pthread_rwlock_unlock(&server_opts.endpt_lock);
2366
2367 return ret;
2368}
2369
2370API int
2371nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2372{
2373 struct nc_endpt *endpt;
2374 int ret = 0;
2375
2376 if (!endpt_name) {
2377 ERRARG("endpt_name");
2378 return -1;
2379 }
2380
2381 /* ENDPT LOCK */
2382 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2383 if (!endpt) {
2384 return -1;
2385 }
2386
2387 if (idle_time > -1) {
2388 endpt->ka.idle_time = idle_time;
2389 }
2390 if (max_probes > -1) {
2391 endpt->ka.max_probes = max_probes;
2392 }
2393 if (probe_interval > -1) {
2394 endpt->ka.probe_interval = probe_interval;
2395 }
2396
2397 /* ENDPT UNLOCK */
2398 pthread_rwlock_unlock(&server_opts.endpt_lock);
2399
2400 return ret;
2401}
2402
Michal Vasko71090fc2016-05-24 16:37:28 +02002403API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +01002404nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002405{
Michal Vasko71090fc2016-05-24 16:37:28 +02002406 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002407 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002408 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002409 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002410 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002411
Michal Vasko93224072021-11-09 12:14:28 +01002412 if (!ctx) {
2413 ERRARG("ctx");
Michal Vasko71090fc2016-05-24 16:37:28 +02002414 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002415 } else if (!session) {
2416 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002417 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002418 }
2419
Michal Vasko93224072021-11-09 12:14:28 +01002420 /* init ctx as needed */
2421 nc_server_init_ctx(ctx);
2422
Michal Vaskoade892d2017-02-22 13:40:35 +01002423 /* BIND LOCK */
2424 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002425
2426 if (!server_opts.endpt_count) {
Michal Vasko05532772021-06-03 12:12:38 +02002427 ERR(NULL, "No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002428 /* BIND UNLOCK */
2429 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002430 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002431 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002432
Michal Vaskoe2713da2016-08-22 16:06:40 +02002433 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002434 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002435 /* BIND UNLOCK */
2436 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002437 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002438 if (!ret) {
2439 return NC_MSG_WOULDBLOCK;
2440 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002441 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002442 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002443
2444 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2445 /* ENDPT READ LOCK */
2446 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2447
2448 /* BIND UNLOCK */
2449 pthread_mutex_unlock(&server_opts.bind_lock);
2450
Michal Vaskob48aa812016-01-18 14:13:09 +01002451 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002452
Michal Vasko131120a2018-05-29 15:44:02 +02002453 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002454 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002455 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002456 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002457 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002458 msgtype = NC_MSG_ERROR;
2459 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002460 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002461 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01002462 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko1a38c862016-01-15 15:50:07 +01002463 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +01002464 (*session)->host = host;
Michal Vasko1a38c862016-01-15 15:50:07 +01002465 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002466
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002467 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002468#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002469 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2470 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002471 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002472 if (ret < 0) {
2473 msgtype = NC_MSG_ERROR;
2474 goto cleanup;
2475 } else if (!ret) {
2476 msgtype = NC_MSG_WOULDBLOCK;
2477 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002478 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002479 } else
2480#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002481#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002482 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2483 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002484 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002485 if (ret < 0) {
2486 msgtype = NC_MSG_ERROR;
2487 goto cleanup;
2488 } else if (!ret) {
2489 msgtype = NC_MSG_WOULDBLOCK;
2490 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002491 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002492 } else
2493#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002494 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2495 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2496 ret = nc_accept_unix(*session, sock);
2497 if (ret < 0) {
2498 msgtype = NC_MSG_ERROR;
2499 goto cleanup;
2500 }
2501 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002502 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002503 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002504 msgtype = NC_MSG_ERROR;
2505 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002506 }
2507
Michal Vasko2cc4c682016-03-01 09:16:48 +01002508 (*session)->data = NULL;
2509
Michal Vaskoade892d2017-02-22 13:40:35 +01002510 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002511 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002512
Michal Vaskob48aa812016-01-18 14:13:09 +01002513 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002514 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002515
Michal Vasko9e036d52016-01-08 10:49:26 +01002516 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002517 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002518 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002519 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002520 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002521 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002522 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002523
roman6ece9c52022-06-22 09:29:17 +02002524 nc_gettimespec_mono_add(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02002525 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
roman6ece9c52022-06-22 09:29:17 +02002526 nc_gettimespec_real_add(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02002527 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002528 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002529
Michal Vasko71090fc2016-05-24 16:37:28 +02002530 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002531
Michal Vasko71090fc2016-05-24 16:37:28 +02002532cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002533 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002534 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002535
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002536 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002537 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002538 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002539}
2540
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002541#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002542
Michal Vaskoadf30f02019-06-24 09:34:47 +02002543/* client is expected to be locked */
2544static int
2545_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 +02002546{
2547 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002548 int ret = -1;
2549
2550 if (!endpt_name) {
2551 /* remove all endpoints */
2552 for (i = 0; i < client->ch_endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002553 free(client->ch_endpts[i].name);
2554 free(client->ch_endpts[i].address);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002555 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 free(client->ch_endpts);
2578 client->ch_endpts = NULL;
2579 client->ch_endpt_count = 0;
2580
2581 ret = 0;
2582 } else {
2583 for (i = 0; i < client->ch_endpt_count; ++i) {
2584 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
Michal Vasko93224072021-11-09 12:14:28 +01002585 free(client->ch_endpts[i].name);
2586 free(client->ch_endpts[i].address);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002587 if (client->ch_endpts[i].sock_pending != -1) {
2588 close(client->ch_endpts[i].sock_pending);
2589 }
2590 switch (client->ch_endpts[i].ti) {
2591#ifdef NC_ENABLED_SSH
2592 case NC_TI_LIBSSH:
2593 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2594 free(client->ch_endpts[i].opts.ssh);
2595 break;
2596#endif
2597#ifdef NC_ENABLED_TLS
2598 case NC_TI_OPENSSL:
2599 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2600 free(client->ch_endpts[i].opts.tls);
2601 break;
2602#endif
2603 default:
2604 ERRINT;
2605 /* won't get here ...*/
2606 break;
2607 }
2608
2609 /* move last endpoint to the empty space */
2610 --client->ch_endpt_count;
2611 if (i < client->ch_endpt_count) {
2612 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2613 } else if (!server_opts.ch_client_count) {
2614 free(server_opts.ch_clients);
2615 server_opts.ch_clients = NULL;
2616 }
2617
2618 ret = 0;
2619 break;
2620 }
2621 }
2622 }
2623
2624 return ret;
2625}
2626
2627API int
2628nc_server_ch_add_client(const char *name)
2629{
2630 uint16_t i;
2631 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002632
2633 if (!name) {
2634 ERRARG("name");
2635 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002636 }
2637
2638 /* WRITE LOCK */
2639 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2640
2641 /* check name uniqueness */
2642 for (i = 0; i < server_opts.ch_client_count; ++i) {
2643 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002644 ERR(NULL, "Call Home client \"%s\" already exists.", name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002645 /* WRITE UNLOCK */
2646 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2647 return -1;
2648 }
2649 }
2650
2651 ++server_opts.ch_client_count;
2652 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2653 if (!server_opts.ch_clients) {
2654 ERRMEM;
2655 /* WRITE UNLOCK */
2656 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2657 return -1;
2658 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002659 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002660
Michal Vasko93224072021-11-09 12:14:28 +01002661 client->name = strdup(name);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002662 client->id = ATOMIC_INC_RELAXED(server_opts.new_client_id);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002663 client->ch_endpts = NULL;
2664 client->ch_endpt_count = 0;
2665 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002666
Michal Vasko2e6defd2016-10-07 15:48:15 +02002667 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002668 client->start_with = NC_CH_FIRST_LISTED;
2669 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002670
Michal Vaskoadf30f02019-06-24 09:34:47 +02002671 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002672
2673 /* WRITE UNLOCK */
2674 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2675
2676 return 0;
2677}
2678
2679API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002680nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002681{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002682 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002683 int ret = -1;
2684
2685 /* WRITE LOCK */
2686 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2687
Michal Vaskoadf30f02019-06-24 09:34:47 +02002688 if (!name) {
2689 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002690 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002691 free(server_opts.ch_clients[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002692
2693 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002694 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002695
2696 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002697 ret = 0;
2698 }
2699 free(server_opts.ch_clients);
2700 server_opts.ch_clients = NULL;
2701
2702 server_opts.ch_client_count = 0;
2703
2704 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002705 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002706 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002707 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko93224072021-11-09 12:14:28 +01002708 free(server_opts.ch_clients[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002709
Michal Vasko2e6defd2016-10-07 15:48:15 +02002710 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002711 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002712
2713 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2714
2715 /* move last client and endpoint(s) to the empty space */
2716 --server_opts.ch_client_count;
2717 if (i < server_opts.ch_client_count) {
2718 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002719 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002720 } else if (!server_opts.ch_client_count) {
2721 free(server_opts.ch_clients);
2722 server_opts.ch_clients = NULL;
2723 }
2724
2725 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002726 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002727 }
2728 }
2729 }
2730
2731 /* WRITE UNLOCK */
2732 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2733
2734 return ret;
2735}
2736
2737API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002738nc_server_ch_is_client(const char *name)
2739{
2740 uint16_t i;
2741 int found = 0;
2742
2743 if (!name) {
2744 return found;
2745 }
2746
2747 /* READ LOCK */
2748 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2749
2750 /* check name uniqueness */
2751 for (i = 0; i < server_opts.ch_client_count; ++i) {
2752 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2753 found = 1;
2754 break;
2755 }
2756 }
2757
2758 /* UNLOCK */
2759 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2760
2761 return found;
2762}
2763
2764API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002765nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002766{
2767 uint16_t i;
2768 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002769 struct nc_ch_endpt *endpt;
2770 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002771
2772 if (!client_name) {
2773 ERRARG("client_name");
2774 return -1;
2775 } else if (!endpt_name) {
2776 ERRARG("endpt_name");
2777 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002778 } else if (!ti) {
2779 ERRARG("ti");
2780 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002781 }
2782
2783 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002784 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002785 if (!client) {
2786 return -1;
2787 }
2788
2789 for (i = 0; i < client->ch_endpt_count; ++i) {
2790 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002791 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002792 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002793 }
2794 }
2795
2796 ++client->ch_endpt_count;
2797 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2798 if (!client->ch_endpts) {
2799 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002800 goto cleanup;
2801 }
2802 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2803
2804 memset(endpt, 0, sizeof *client->ch_endpts);
Michal Vasko93224072021-11-09 12:14:28 +01002805 endpt->name = strdup(endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002806 endpt->ti = ti;
2807 endpt->sock_pending = -1;
2808 endpt->ka.idle_time = 1;
2809 endpt->ka.max_probes = 10;
2810 endpt->ka.probe_interval = 5;
2811
2812 switch (ti) {
2813#ifdef NC_ENABLED_SSH
2814 case NC_TI_LIBSSH:
2815 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2816 if (!endpt->opts.ssh) {
2817 ERRMEM;
2818 goto cleanup;
2819 }
2820 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2821 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002822 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002823 break;
2824#endif
2825#ifdef NC_ENABLED_TLS
2826 case NC_TI_OPENSSL:
2827 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2828 if (!endpt->opts.tls) {
2829 ERRMEM;
2830 goto cleanup;
2831 }
2832 break;
2833#endif
2834 default:
2835 ERRINT;
2836 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002837 }
2838
Michal Vaskoadf30f02019-06-24 09:34:47 +02002839 /* success */
2840 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002841
Michal Vaskoadf30f02019-06-24 09:34:47 +02002842cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002843 /* UNLOCK */
2844 nc_server_ch_client_unlock(client);
2845
Michal Vaskoadf30f02019-06-24 09:34:47 +02002846 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002847}
2848
2849API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002850nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002851{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002852 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002853 struct nc_ch_client *client;
2854
2855 if (!client_name) {
2856 ERRARG("client_name");
2857 return -1;
2858 }
2859
2860 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002861 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002862 if (!client) {
2863 return -1;
2864 }
2865
Michal Vaskoadf30f02019-06-24 09:34:47 +02002866 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002867
2868 /* UNLOCK */
2869 nc_server_ch_client_unlock(client);
2870
2871 return ret;
2872}
2873
2874API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002875nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2876{
2877 uint16_t i;
2878 struct nc_ch_client *client = NULL;
2879 int found = 0;
2880
2881 if (!client_name || !endpt_name) {
2882 return found;
2883 }
2884
2885 /* READ LOCK */
2886 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2887
2888 for (i = 0; i < server_opts.ch_client_count; ++i) {
2889 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2890 client = &server_opts.ch_clients[i];
2891 break;
2892 }
2893 }
2894
2895 if (!client) {
2896 goto cleanup;
2897 }
2898
2899 for (i = 0; i < client->ch_endpt_count; ++i) {
2900 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2901 found = 1;
2902 break;
2903 }
2904 }
2905
2906cleanup:
2907 /* UNLOCK */
2908 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2909 return found;
2910}
2911
2912API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002913nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
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 (!address) {
2925 ERRARG("address");
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 Vasko93224072021-11-09 12:14:28 +01002935 free(endpt->address);
2936 endpt->address = strdup(address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002937
2938 /* UNLOCK */
2939 nc_server_ch_client_unlock(client);
2940
Michal Vaskoadf30f02019-06-24 09:34:47 +02002941 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002942}
2943
2944API int
2945nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2946{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002947 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002948 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002949
2950 if (!client_name) {
2951 ERRARG("client_name");
2952 return -1;
2953 } else if (!endpt_name) {
2954 ERRARG("endpt_name");
2955 return -1;
2956 } else if (!port) {
2957 ERRARG("port");
2958 return -1;
2959 }
2960
2961 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002962 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2963 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002964 return -1;
2965 }
2966
Michal Vaskoadf30f02019-06-24 09:34:47 +02002967 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002968
2969 /* UNLOCK */
2970 nc_server_ch_client_unlock(client);
2971
ravsz5c5a4422020-03-31 15:53:21 +02002972 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002973}
2974
2975API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002976nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
2977{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002978 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002979 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002980
2981 if (!client_name) {
2982 ERRARG("client_name");
2983 return -1;
2984 } else if (!endpt_name) {
2985 ERRARG("endpt_name");
2986 return -1;
2987 }
2988
2989 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002990 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2991 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002992 return -1;
2993 }
2994
Michal Vaskoadf30f02019-06-24 09:34:47 +02002995 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002996
2997 /* UNLOCK */
2998 nc_server_ch_client_unlock(client);
2999
Michal Vasko9af829a2019-09-12 13:50:00 +02003000 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003001}
3002
3003API int
3004nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
3005 int probe_interval)
3006{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003007 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003008 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003009
3010 if (!client_name) {
3011 ERRARG("client_name");
3012 return -1;
3013 } else if (!endpt_name) {
3014 ERRARG("endpt_name");
3015 return -1;
3016 }
3017
3018 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003019 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3020 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003021 return -1;
3022 }
3023
Michal Vaskoadf30f02019-06-24 09:34:47 +02003024 if (idle_time > -1) {
3025 endpt->ka.idle_time = idle_time;
3026 }
3027 if (max_probes > -1) {
3028 endpt->ka.max_probes = max_probes;
3029 }
3030 if (probe_interval > -1) {
3031 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003032 }
3033
3034 /* UNLOCK */
3035 nc_server_ch_client_unlock(client);
3036
Michal Vasko9af829a2019-09-12 13:50:00 +02003037 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003038}
3039
3040API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003041nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
3042{
3043 struct nc_ch_client *client;
3044
3045 if (!client_name) {
3046 ERRARG("client_name");
3047 return -1;
3048 } else if (!conn_type) {
3049 ERRARG("conn_type");
3050 return -1;
3051 }
3052
3053 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003054 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003055 if (!client) {
3056 return -1;
3057 }
3058
3059 if (client->conn_type != conn_type) {
3060 client->conn_type = conn_type;
3061
3062 /* set default options */
3063 switch (conn_type) {
3064 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003065 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003066 break;
3067 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003068 client->conn.period.period = 60;
3069 client->conn.period.anchor_time = 0;
3070 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003071 break;
3072 default:
3073 ERRINT;
3074 break;
3075 }
3076 }
3077
3078 /* UNLOCK */
3079 nc_server_ch_client_unlock(client);
3080
3081 return 0;
3082}
3083
3084API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003085nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
3086{
3087 struct nc_ch_client *client;
3088
3089 if (!client_name) {
3090 ERRARG("client_name");
3091 return -1;
3092 } else if (!period) {
3093 ERRARG("period");
3094 return -1;
3095 }
3096
3097 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003098 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003099 if (!client) {
3100 return -1;
3101 }
3102
3103 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003104 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003105 /* UNLOCK */
3106 nc_server_ch_client_unlock(client);
3107 return -1;
3108 }
3109
3110 client->conn.period.period = period;
3111
3112 /* UNLOCK */
3113 nc_server_ch_client_unlock(client);
3114
3115 return 0;
3116}
3117
3118API int
3119nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003120{
3121 struct nc_ch_client *client;
3122
3123 if (!client_name) {
3124 ERRARG("client_name");
3125 return -1;
3126 }
3127
3128 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003129 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003130 if (!client) {
3131 return -1;
3132 }
3133
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003134 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003135 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003136 /* UNLOCK */
3137 nc_server_ch_client_unlock(client);
3138 return -1;
3139 }
3140
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003141 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003142
3143 /* UNLOCK */
3144 nc_server_ch_client_unlock(client);
3145
3146 return 0;
3147}
3148
3149API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003150nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003151{
3152 struct nc_ch_client *client;
3153
3154 if (!client_name) {
3155 ERRARG("client_name");
3156 return -1;
3157 }
3158
3159 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003160 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003161 if (!client) {
3162 return -1;
3163 }
3164
3165 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003166 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003167 /* UNLOCK */
3168 nc_server_ch_client_unlock(client);
3169 return -1;
3170 }
3171
3172 client->conn.period.idle_timeout = idle_timeout;
3173
3174 /* UNLOCK */
3175 nc_server_ch_client_unlock(client);
3176
3177 return 0;
3178}
3179
3180API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003181nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
3182{
3183 struct nc_ch_client *client;
3184
3185 if (!client_name) {
3186 ERRARG("client_name");
3187 return -1;
3188 }
3189
3190 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003191 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003192 if (!client) {
3193 return -1;
3194 }
3195
3196 client->start_with = start_with;
3197
3198 /* UNLOCK */
3199 nc_server_ch_client_unlock(client);
3200
3201 return 0;
3202}
3203
3204API int
3205nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
3206{
3207 struct nc_ch_client *client;
3208
3209 if (!client_name) {
3210 ERRARG("client_name");
3211 return -1;
3212 } else if (!max_attempts) {
3213 ERRARG("max_attempts");
3214 return -1;
3215 }
3216
3217 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003218 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003219 if (!client) {
3220 return -1;
3221 }
3222
3223 client->max_attempts = max_attempts;
3224
3225 /* UNLOCK */
3226 nc_server_ch_client_unlock(client);
3227
3228 return 0;
3229}
3230
3231/* client lock is expected to be held */
3232static NC_MSG_TYPE
Michal Vasko58bac1c2022-03-24 15:25:26 +01003233nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
3234 nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01003235{
Michal Vasko71090fc2016-05-24 16:37:28 +02003236 NC_MSG_TYPE msgtype;
Michal Vasko58bac1c2022-03-24 15:25:26 +01003237 const struct ly_ctx *ctx = NULL;
Michal Vaskob05053d2016-01-22 16:12:06 +01003238 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02003239 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01003240 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01003241
Michal Vasko4c612cd2021-02-05 08:53:42 +01003242 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 +01003243 if (sock < 0) {
Michal Vasko0b30e452021-03-03 10:30:15 +01003244 if (endpt->sock_pending > -1) {
3245 ++endpt->sock_retries;
3246 if (endpt->sock_retries == NC_SOCKET_CH_RETRIES) {
Michal Vasko05532772021-06-03 12:12:38 +02003247 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 +01003248 close(endpt->sock_pending);
3249 endpt->sock_pending = -1;
3250 endpt->sock_retries = 0;
3251 }
Michal Vasko4c612cd2021-02-05 08:53:42 +01003252 }
Michal Vasko71090fc2016-05-24 16:37:28 +02003253 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003254 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00003255 /* no need to store the socket as pending any longer */
3256 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01003257
Michal Vasko93224072021-11-09 12:14:28 +01003258 /* acquire context */
3259 ctx = acquire_ctx_cb(ctx_cb_data);
3260 if (!ctx) {
3261 ERR(NULL, "Failed to acquire context for a new Call Home session.");
3262 close(sock);
3263 free(ip_host);
3264 return NC_MSG_ERROR;
3265 }
3266
3267 /* create session */
Michal Vasko131120a2018-05-29 15:44:02 +02003268 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01003269 if (!(*session)) {
3270 ERRMEM;
3271 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01003272 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02003273 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003274 }
3275 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01003276 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003277 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +01003278 (*session)->host = ip_host;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003279 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01003280
Michal Vaskob05053d2016-01-22 16:12:06 +01003281 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01003282#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02003283 if (endpt->ti == NC_TI_LIBSSH) {
3284 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01003285 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003286 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003287
Michal Vasko71090fc2016-05-24 16:37:28 +02003288 if (ret < 0) {
3289 msgtype = NC_MSG_ERROR;
3290 goto fail;
3291 } else if (!ret) {
3292 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003293 goto fail;
3294 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003295 } else
3296#endif
Radek Krejci53691be2016-02-22 13:58:37 +01003297#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02003298 if (endpt->ti == NC_TI_OPENSSL) {
3299 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01003300 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003301 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003302
Michal Vasko71090fc2016-05-24 16:37:28 +02003303 if (ret < 0) {
3304 msgtype = NC_MSG_ERROR;
3305 goto fail;
3306 } else if (!ret) {
3307 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003308 goto fail;
3309 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003310 } else
3311#endif
3312 {
Michal Vaskob05053d2016-01-22 16:12:06 +01003313 ERRINT;
3314 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003315 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003316 goto fail;
3317 }
3318
3319 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02003320 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003321
3322 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003323 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003324 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003325 goto fail;
3326 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003327
roman6ece9c52022-06-22 09:29:17 +02003328 nc_gettimespec_mono_add(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02003329 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
roman6ece9c52022-06-22 09:29:17 +02003330 nc_gettimespec_real_add(&ts_cur, 0);
Michal Vasko9fb42272017-10-05 13:50:05 +02003331 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003332 (*session)->status = NC_STATUS_RUNNING;
3333
Michal Vasko71090fc2016-05-24 16:37:28 +02003334 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003335
3336fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003337 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003338 *session = NULL;
Michal Vasko58bac1c2022-03-24 15:25:26 +01003339 if (ctx) {
3340 release_ctx_cb(ctx_cb_data);
3341 }
Michal Vasko71090fc2016-05-24 16:37:28 +02003342 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003343}
3344
Michal Vasko2e6defd2016-10-07 15:48:15 +02003345struct nc_ch_client_thread_arg {
3346 char *client_name;
Michal Vasko93224072021-11-09 12:14:28 +01003347 nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb;
3348 nc_server_ch_session_release_ctx_cb release_ctx_cb;
3349 void *ctx_cb_data;
3350 nc_server_ch_new_session_cb new_session_cb;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003351};
3352
3353static struct nc_ch_client *
3354nc_server_ch_client_with_endpt_lock(const char *name)
3355{
3356 struct nc_ch_client *client;
3357
3358 while (1) {
3359 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003360 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003361 if (!client) {
3362 return NULL;
3363 }
3364 if (client->ch_endpt_count) {
3365 return client;
3366 }
3367 /* no endpoints defined yet */
3368
3369 /* UNLOCK */
3370 nc_server_ch_client_unlock(client);
3371
3372 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3373 }
3374
3375 return NULL;
3376}
3377
3378static int
3379nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3380{
Michal Vasko3f05a092018-03-13 10:39:49 +01003381 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003382 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003383 struct timespec ts;
3384 struct nc_ch_client *client;
3385
Michal Vasko2e6defd2016-10-07 15:48:15 +02003386 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003387 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003388
Michal Vaskofeccb312022-03-24 15:24:59 +01003389 session->flags |= NC_SESSION_CH_THREAD;
Michal Vasko0db3db52021-03-03 10:45:42 +01003390
Michal Vasko2e6defd2016-10-07 15:48:15 +02003391 /* give the session to the user */
Michal Vasko93224072021-11-09 12:14:28 +01003392 if (data->new_session_cb(data->client_name, session)) {
Michal Vaskof1c26c22021-04-12 16:34:33 +02003393 /* something is wrong, free the session */
Michal Vaskofeccb312022-03-24 15:24:59 +01003394 session->flags &= ~NC_SESSION_CH_THREAD;
Michal Vaskof1c26c22021-04-12 16:34:33 +02003395
3396 /* CH UNLOCK */
3397 pthread_mutex_unlock(&session->opts.server.ch_lock);
3398
Michal Vasko58bac1c2022-03-24 15:25:26 +01003399 /* session terminated, release its context */
Michal Vaskof1c26c22021-04-12 16:34:33 +02003400 nc_session_free(session, NULL);
Michal Vasko58bac1c2022-03-24 15:25:26 +01003401 data->release_ctx_cb(data->ctx_cb_data);
3402 return ret;
Michal Vaskof1c26c22021-04-12 16:34:33 +02003403 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003404
3405 do {
roman6ece9c52022-06-22 09:29:17 +02003406 nc_gettimespec_real_add(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003407
Michal Vasko0db3db52021-03-03 10:45:42 +01003408 /* CH COND WAIT */
Michal Vaskoacf98472021-02-04 15:33:57 +01003409 r = pthread_cond_timedwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, &ts);
Michal Vasko3f05a092018-03-13 10:39:49 +01003410 if (!r) {
3411 /* we were woken up, something probably happened */
3412 if (session->status != NC_STATUS_RUNNING) {
3413 break;
3414 }
3415 } else if (r != ETIMEDOUT) {
Michal Vasko05532772021-06-03 12:12:38 +02003416 ERR(session, "Pthread condition timedwait failed (%s).", strerror(r));
Michal Vasko3f05a092018-03-13 10:39:49 +01003417 ret = -1;
3418 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003419 }
3420
Michal Vasko2e6defd2016-10-07 15:48:15 +02003421 /* check whether the client was not removed */
3422 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003423 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003424 if (!client) {
3425 /* client was removed, finish thread */
Michal Vasko05532772021-06-03 12:12:38 +02003426 VRB(session, "Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003427 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003428 ret = 1;
3429 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003430 }
3431
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003432 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003433 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003434 } else {
3435 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003436 }
3437
roman6ece9c52022-06-22 09:29:17 +02003438 nc_gettimespec_mono_add(&ts, 0);
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003439 if (!nc_session_get_notif_status(session) && idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
Michal Vasko05532772021-06-03 12:12:38 +02003440 VRB(session, "Call Home client \"%s\": session idle timeout elapsed.", client->name);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003441 session->status = NC_STATUS_INVALID;
3442 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3443 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003444
3445 /* UNLOCK */
3446 nc_server_ch_client_unlock(client);
3447
3448 } while (session->status == NC_STATUS_RUNNING);
3449
Michal Vaskofeccb312022-03-24 15:24:59 +01003450 /* signal to nc_session_free() that CH thread is terminating */
3451 session->flags &= ~NC_SESSION_CH_THREAD;
3452 pthread_cond_signal(&session->opts.server.ch_cond);
Michal Vasko0db3db52021-03-03 10:45:42 +01003453
Michal Vasko27377422018-03-15 08:59:35 +01003454 /* CH UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003455 pthread_mutex_unlock(&session->opts.server.ch_lock);
Michal Vasko27377422018-03-15 08:59:35 +01003456
Michal Vasko395900e2021-11-09 12:14:28 +01003457 /* session terminated, release its context */
3458 data->release_ctx_cb(data->ctx_cb_data);
3459
Michal Vasko3f05a092018-03-13 10:39:49 +01003460 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003461}
3462
3463static void *
3464nc_ch_client_thread(void *arg)
3465{
3466 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3467 NC_MSG_TYPE msgtype;
3468 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003469 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003470 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003471 struct nc_ch_endpt *cur_endpt;
3472 struct nc_session *session;
3473 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003474 uint32_t client_id;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003475 time_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003476
3477 /* LOCK */
3478 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3479 if (!client) {
3480 goto cleanup;
3481 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003482 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003483
3484 cur_endpt = &client->ch_endpts[0];
3485 cur_endpt_name = strdup(cur_endpt->name);
3486
Michal Vasko05532772021-06-03 12:12:38 +02003487 VRB(NULL, "Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003488 while (1) {
Michal Vasko58bac1c2022-03-24 15:25:26 +01003489 msgtype = nc_connect_ch_endpt(cur_endpt, data->acquire_ctx_cb, data->release_ctx_cb, data->ctx_cb_data, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003490
3491 if (msgtype == NC_MSG_HELLO) {
3492 /* UNLOCK */
3493 nc_server_ch_client_unlock(client);
3494
Michal Vasko05532772021-06-03 12:12:38 +02003495 VRB(NULL, "Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003496 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3497 goto cleanup;
3498 }
Michal Vasko05532772021-06-03 12:12:38 +02003499 VRB(NULL, "Call Home client \"%s\" session terminated.", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003500
3501 /* LOCK */
3502 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3503 if (!client) {
3504 goto cleanup;
3505 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003506 if (client->id != client_id) {
3507 nc_server_ch_client_unlock(client);
3508 goto cleanup;
3509 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003510
3511 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003512 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003513 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko18e1fa02021-11-29 09:02:05 +01003514 if (client->conn.period.anchor_time) {
3515 /* anchored */
3516 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
3517 } else {
3518 /* fixed timeout */
3519 reconnect_in = client->conn.period.period * 60;
3520 }
3521
Michal Vasko2e6defd2016-10-07 15:48:15 +02003522 /* UNLOCK */
3523 nc_server_ch_client_unlock(client);
3524
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003525 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
Michal Vasko05532772021-06-03 12:12:38 +02003526 VRB(NULL, "Call Home client \"%s\" reconnecting in %d seconds.", data->client_name, reconnect_in);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003527 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003528
3529 /* LOCK */
3530 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3531 if (!client) {
3532 goto cleanup;
3533 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003534 if (client->id != client_id) {
3535 nc_server_ch_client_unlock(client);
3536 goto cleanup;
3537 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003538 }
3539
3540 /* set next endpoint to try */
3541 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003542 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003543 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003544 /* we keep the current one but due to unlock/lock we have to find it again */
3545 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3546 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3547 break;
3548 }
3549 }
3550 if (next_endpt_index >= client->ch_endpt_count) {
3551 /* endpoint was removed, start with the first one */
3552 next_endpt_index = 0;
3553 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003554 } else {
3555 /* just get a random index */
3556 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003557 }
3558
Michal Vasko2e6defd2016-10-07 15:48:15 +02003559 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003560 /* UNLOCK */
3561 nc_server_ch_client_unlock(client);
3562
Michal Vasko2e6defd2016-10-07 15:48:15 +02003563 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003564 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3565
Michal Vasko6bb116b2016-10-26 13:53:46 +02003566 /* LOCK */
3567 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3568 if (!client) {
3569 goto cleanup;
3570 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003571 if (client->id != client_id) {
3572 nc_server_ch_client_unlock(client);
3573 goto cleanup;
3574 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003575
Michal Vasko2e6defd2016-10-07 15:48:15 +02003576 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003577
3578 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003579 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3580 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003581 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003582 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003583 }
3584
Peter Feiged05f2252018-09-03 08:09:47 +00003585 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003586 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003587 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003588 cur_attempts = 0;
3589 } else if (cur_attempts == client->max_attempts) {
3590 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003591 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003592 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003593 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003594 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003595 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003596 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003597 }
3598
3599 cur_attempts = 0;
3600 } /* else we keep the current one */
3601 }
Peter Feiged05f2252018-09-03 08:09:47 +00003602
3603 cur_endpt = &client->ch_endpts[next_endpt_index];
3604 free(cur_endpt_name);
3605 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003606 }
3607
3608cleanup:
Michal Vasko05532772021-06-03 12:12:38 +02003609 VRB(NULL, "Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003610 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003611 free(data->client_name);
3612 free(data);
3613 return NULL;
3614}
3615
3616API int
Michal Vasko93224072021-11-09 12:14:28 +01003617nc_connect_ch_client_dispatch(const char *client_name, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
3618 nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb)
Michal Vasko3f05a092018-03-13 10:39:49 +01003619{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003620 int ret;
3621 pthread_t tid;
3622 struct nc_ch_client_thread_arg *arg;
3623
3624 if (!client_name) {
3625 ERRARG("client_name");
3626 return -1;
Michal Vasko93224072021-11-09 12:14:28 +01003627 } else if (!acquire_ctx_cb) {
3628 ERRARG("acquire_ctx_cb");
3629 return -1;
3630 } else if (!release_ctx_cb) {
3631 ERRARG("release_ctx_cb");
3632 return -1;
3633 } else if (!new_session_cb) {
3634 ERRARG("new_session_cb");
Michal Vasko2e6defd2016-10-07 15:48:15 +02003635 return -1;
3636 }
3637
3638 arg = malloc(sizeof *arg);
3639 if (!arg) {
3640 ERRMEM;
3641 return -1;
3642 }
3643 arg->client_name = strdup(client_name);
3644 if (!arg->client_name) {
3645 ERRMEM;
3646 free(arg);
3647 return -1;
3648 }
Michal Vasko93224072021-11-09 12:14:28 +01003649 arg->acquire_ctx_cb = acquire_ctx_cb;
3650 arg->release_ctx_cb = release_ctx_cb;
3651 arg->ctx_cb_data = ctx_cb_data;
3652 arg->new_session_cb = new_session_cb;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003653
3654 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3655 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02003656 ERR(NULL, "Creating a new thread failed (%s).", strerror(ret));
Michal Vasko2e6defd2016-10-07 15:48:15 +02003657 free(arg->client_name);
3658 free(arg);
3659 return -1;
3660 }
3661 /* the thread now manages arg */
3662
3663 pthread_detach(tid);
3664
3665 return 0;
3666}
3667
Radek Krejci53691be2016-02-22 13:58:37 +01003668#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003669
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003670API time_t
3671nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003672{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003673 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003674 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003675 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003676 }
3677
Michal Vasko2e6defd2016-10-07 15:48:15 +02003678 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003679}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003680
3681API void
Michal Vasko71dbd772021-03-23 14:08:37 +01003682nc_session_inc_notif_status(struct nc_session *session)
Michal Vasko3486a7c2017-03-03 13:28:07 +01003683{
3684 if (!session || (session->side != NC_SERVER)) {
3685 ERRARG("session");
3686 return;
3687 }
3688
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003689 /* NTF STATUS LOCK */
3690 pthread_mutex_lock(&session->opts.server.ntf_status_lock);
3691
Michal Vasko71dbd772021-03-23 14:08:37 +01003692 ++session->opts.server.ntf_status;
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003693
3694 /* NTF STATUS UNLOCK */
3695 pthread_mutex_unlock(&session->opts.server.ntf_status_lock);
Michal Vasko71dbd772021-03-23 14:08:37 +01003696}
3697
3698API void
3699nc_session_dec_notif_status(struct nc_session *session)
3700{
3701 if (!session || (session->side != NC_SERVER)) {
3702 ERRARG("session");
3703 return;
3704 }
3705
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003706 /* NTF STATUS LOCK */
3707 pthread_mutex_lock(&session->opts.server.ntf_status_lock);
3708
Michal Vasko71dbd772021-03-23 14:08:37 +01003709 if (session->opts.server.ntf_status) {
3710 --session->opts.server.ntf_status;
3711 }
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003712
3713 /* NTF STATUS UNLOCK */
3714 pthread_mutex_unlock(&session->opts.server.ntf_status_lock);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003715}
3716
3717API int
3718nc_session_get_notif_status(const struct nc_session *session)
3719{
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003720 uint32_t ntf_status;
3721
Michal Vasko3486a7c2017-03-03 13:28:07 +01003722 if (!session || (session->side != NC_SERVER)) {
3723 ERRARG("session");
3724 return 0;
3725 }
3726
Michal Vaskodf68e7e2022-04-21 11:04:00 +02003727 /* NTF STATUS LOCK */
3728 pthread_mutex_lock(&((struct nc_session *)session)->opts.server.ntf_status_lock);
3729
3730 ntf_status = session->opts.server.ntf_status;
3731
3732 /* NTF STATUS UNLOCK */
3733 pthread_mutex_unlock(&((struct nc_session *)session)->opts.server.ntf_status_lock);
3734
3735 return ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003736}
Michal Vasko8f430592019-02-26 08:32:54 +01003737
3738API int
3739nc_session_is_callhome(const struct nc_session *session)
3740{
3741 if (!session || (session->side != NC_SERVER)) {
3742 ERRARG("session");
3743 return 0;
3744 }
3745
3746 if (session->flags & NC_SESSION_CALLHOME) {
3747 return 1;
3748 }
3749
3750 return 0;
3751}