blob: 907c3caa01cce7763f5298326e15ae5e2c1599bb [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 Vasko05ba9df2016-01-13 14:40:27 +0100569static 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 Vasko93224072021-11-09 12:14:28 +0100654 lydict_insert_zc(session->ctx, model_data, (const char **)&model_data);
Michal Vasko9b1a9522021-03-15 16:24:26 +0100655 if (lyd_new_any(data, NULL, "data", model_data, 1, LYD_ANYDATA_STRING, 1, NULL)) {
656 ERRINT;
Michal Vasko93224072021-11-09 12:14:28 +0100657 lydict_remove(session->ctx, model_data);
Michal Vasko9b1a9522021-03-15 16:24:26 +0100658 lyd_free_tree(data);
659 return NULL;
660 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100661
Radek Krejci36dfdb32016-09-01 16:56:35 +0200662 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100663}
664
665static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100666nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100667{
Michal Vasko428087d2016-01-14 16:04:28 +0100668 session->term_reason = NC_SESSION_TERM_CLOSED;
669 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100670}
671
Michal Vasko93224072021-11-09 12:14:28 +0100672/**
673 * @brief Initialize a context with default RPC callbacks if none are set.
674 *
675 * @param[in] ctx Context to initialize.
676 */
677static void
678nc_server_init_ctx(const struct ly_ctx *ctx)
Michal Vasko086311b2016-01-08 09:53:11 +0100679{
Michal Vasko77367452021-02-16 16:32:18 +0100680 struct lysc_node *rpc;
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100681
Michal Vasko05ba9df2016-01-13 14:40:27 +0100682 /* set default <get-schema> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100683 rpc = NULL;
684 if (ly_ctx_get_module_implemented(ctx, "ietf-netconf-monitoring")) {
685 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
686 }
Michal Vasko88639e92017-08-03 14:38:10 +0200687 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100688 rpc->priv = nc_clb_default_get_schema;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100689 }
690
Michal Vasko93224072021-11-09 12:14:28 +0100691 /* set default <close-session> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100692 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf:close-session", 0);
Michal Vasko88639e92017-08-03 14:38:10 +0200693 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100694 rpc->priv = nc_clb_default_close_session;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100695 }
Michal Vasko93224072021-11-09 12:14:28 +0100696}
Michal Vasko05ba9df2016-01-13 14:40:27 +0100697
Michal Vasko93224072021-11-09 12:14:28 +0100698API int
699nc_server_init(void)
700{
701 pthread_rwlockattr_t attr, *attr_p = NULL;
702 int r;
703
704 nc_init();
Michal Vaskob48aa812016-01-18 14:13:09 +0100705
706 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500707 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100708
Michal Vasko93224072021-11-09 12:14:28 +0100709#ifdef HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
710 if ((r = pthread_rwlockattr_init(&attr))) {
711 ERR(NULL, "%s: failed init attribute (%s).", __func__, strerror(r));
712 goto error;
713 }
714 attr_p = &attr;
715 if ((r = pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))) {
716 ERR(NULL, "%s: failed set attribute (%s).", __func__, strerror(r));
717 goto error;
718 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700719#endif
Michal Vasko93224072021-11-09 12:14:28 +0100720
721 if ((r = pthread_rwlock_init(&server_opts.endpt_lock, attr_p))) {
722 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
723 goto error;
724 }
725 if ((r = pthread_rwlock_init(&server_opts.ch_client_lock, attr_p))) {
726 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
727 goto error;
728 }
729
730 if (attr_p) {
731 pthread_rwlockattr_destroy(attr_p);
Frank Rimpler9f838b02018-07-25 06:44:03 +0000732 }
Michal Vasko086311b2016-01-08 09:53:11 +0100733 return 0;
Michal Vasko93224072021-11-09 12:14:28 +0100734
735error:
736 if (attr_p) {
737 pthread_rwlockattr_destroy(attr_p);
738 }
739 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100740}
741
Michal Vaskob48aa812016-01-18 14:13:09 +0100742API void
743nc_server_destroy(void)
744{
Michal Vasko1440a742021-03-31 11:11:03 +0200745 uint32_t i;
Radek Krejci658782b2016-12-04 22:04:55 +0100746
747 for (i = 0; i < server_opts.capabilities_count; i++) {
Michal Vasko93224072021-11-09 12:14:28 +0100748 free(server_opts.capabilities[i]);
Radek Krejci658782b2016-12-04 22:04:55 +0100749 }
750 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200751 server_opts.capabilities = NULL;
752 server_opts.capabilities_count = 0;
Michal Vasko1440a742021-03-31 11:11:03 +0200753 if (server_opts.content_id_data && server_opts.content_id_data_free) {
754 server_opts.content_id_data_free(server_opts.content_id_data);
755 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200756
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200757#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100758 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200759 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100760#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100761#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100762 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
763 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
764 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200765 server_opts.passwd_auth_data = NULL;
766 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100767
Michal Vasko17dfda92016-12-01 14:06:16 +0100768 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100769
770 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
771 server_opts.hostkey_data_free(server_opts.hostkey_data);
772 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200773 server_opts.hostkey_data = NULL;
774 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100775#endif
776#ifdef NC_ENABLED_TLS
777 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
778 server_opts.server_cert_data_free(server_opts.server_cert_data);
779 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200780 server_opts.server_cert_data = NULL;
781 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100782 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
783 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
784 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200785 server_opts.trusted_cert_list_data = NULL;
786 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100787#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100788 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100789}
790
Michal Vasko086311b2016-01-08 09:53:11 +0100791API int
792nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
793{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200794 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
795 ERRARG("basic_mode");
796 return -1;
797 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
798 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100799 return -1;
800 }
801
802 server_opts.wd_basic_mode = basic_mode;
803 server_opts.wd_also_supported = also_supported;
804 return 0;
805}
806
Michal Vasko1a38c862016-01-15 15:50:07 +0100807API void
Michal Vasko55f03972016-04-13 08:56:01 +0200808nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
809{
810 if (!basic_mode && !also_supported) {
811 ERRARG("basic_mode and also_supported");
812 return;
813 }
814
815 if (basic_mode) {
816 *basic_mode = server_opts.wd_basic_mode;
817 }
818 if (also_supported) {
819 *also_supported = server_opts.wd_also_supported;
820 }
821}
822
Michal Vasko55f03972016-04-13 08:56:01 +0200823API int
Radek Krejci658782b2016-12-04 22:04:55 +0100824nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200825{
Michal Vasko93224072021-11-09 12:14:28 +0100826 void *mem;
Radek Krejci658782b2016-12-04 22:04:55 +0100827
828 if (!value || !value[0]) {
829 ERRARG("value must not be empty");
830 return EXIT_FAILURE;
831 }
832
Michal Vasko93224072021-11-09 12:14:28 +0100833 mem = realloc(server_opts.capabilities, (server_opts.capabilities_count + 1) * sizeof *server_opts.capabilities);
834 if (!mem) {
Radek Krejci658782b2016-12-04 22:04:55 +0100835 ERRMEM;
836 return EXIT_FAILURE;
837 }
Michal Vasko93224072021-11-09 12:14:28 +0100838 server_opts.capabilities = mem;
839
840 server_opts.capabilities[server_opts.capabilities_count] = strdup(value);
841 server_opts.capabilities_count++;
Radek Krejci658782b2016-12-04 22:04:55 +0100842
843 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200844}
845
Michal Vasko1a38c862016-01-15 15:50:07 +0100846API void
Michal Vasko1440a742021-03-31 11:11:03 +0200847nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
848 void (*free_user_data)(void *user_data))
849{
850 server_opts.content_id_clb = content_id_clb;
851 server_opts.content_id_data = user_data;
852 server_opts.content_id_data_free = free_user_data;
853}
854
855API void
Michal Vasko086311b2016-01-08 09:53:11 +0100856nc_server_set_hello_timeout(uint16_t hello_timeout)
857{
Michal Vasko086311b2016-01-08 09:53:11 +0100858 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100859}
860
Michal Vasko55f03972016-04-13 08:56:01 +0200861API uint16_t
862nc_server_get_hello_timeout(void)
863{
864 return server_opts.hello_timeout;
865}
866
Michal Vasko1a38c862016-01-15 15:50:07 +0100867API void
Michal Vasko086311b2016-01-08 09:53:11 +0100868nc_server_set_idle_timeout(uint16_t idle_timeout)
869{
Michal Vasko086311b2016-01-08 09:53:11 +0100870 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100871}
872
Michal Vasko55f03972016-04-13 08:56:01 +0200873API uint16_t
874nc_server_get_idle_timeout(void)
875{
876 return server_opts.idle_timeout;
877}
878
Michal Vasko71090fc2016-05-24 16:37:28 +0200879API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +0100880nc_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 +0100881{
Michal Vasko71090fc2016-05-24 16:37:28 +0200882 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200883 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200884
Michal Vasko93224072021-11-09 12:14:28 +0100885 if (!ctx) {
886 ERRARG("ctx");
Michal Vasko71090fc2016-05-24 16:37:28 +0200887 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200888 } else if (fdin < 0) {
889 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200890 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200891 } else if (fdout < 0) {
892 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200893 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200894 } else if (!username) {
895 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200896 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200897 } else if (!session) {
898 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200899 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100900 }
901
Michal Vasko93224072021-11-09 12:14:28 +0100902 /* init ctx as needed */
903 nc_server_init_ctx(ctx);
904
Michal Vasko086311b2016-01-08 09:53:11 +0100905 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200906 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100907 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100908 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200909 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100910 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100911 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100912
Michal Vasko086311b2016-01-08 09:53:11 +0100913 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100914 (*session)->ti_type = NC_TI_FD;
915 (*session)->ti.fd.in = fdin;
916 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100917
Michal Vasko93224072021-11-09 12:14:28 +0100918 /* assign context */
Michal Vasko1a38c862016-01-15 15:50:07 +0100919 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +0100920 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100921
Michal Vaskob48aa812016-01-18 14:13:09 +0100922 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200923 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100924
Michal Vasko086311b2016-01-08 09:53:11 +0100925 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200926 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200927 if (msgtype != NC_MSG_HELLO) {
928 nc_session_free(*session, NULL);
929 *session = NULL;
930 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100931 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200932
933 nc_gettimespec_mono(&ts_cur);
934 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
935 nc_gettimespec_real(&ts_cur);
936 (*session)->opts.server.session_start = ts_cur.tv_sec;
937
Michal Vasko1a38c862016-01-15 15:50:07 +0100938 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100939
Michal Vasko71090fc2016-05-24 16:37:28 +0200940 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100941}
Michal Vasko9e036d52016-01-08 10:49:26 +0100942
Michal Vaskob30b99c2016-07-26 11:35:43 +0200943static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100944nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
945{
946 uint8_t q_last;
947
948 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
949 ERRINT;
950 return;
951 }
952
953 /* get a unique queue value (by adding 1 to the last added value, if any) */
954 if (ps->queue_len) {
955 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
956 *id = ps->queue[q_last] + 1;
957 } else {
958 *id = 0;
959 }
960
961 /* add the id into the queue */
962 ++ps->queue_len;
963 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
964 ps->queue[q_last] = *id;
965}
966
967static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200968nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
969{
Michal Vasko74c345f2018-02-07 10:37:11 +0100970 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200971
972 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100973 /* get the actual queue idx */
974 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200975
976 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100977 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200978 /* another equal value, simply cannot be */
979 ERRINT;
980 }
Michal Vaskod8340032018-02-12 14:41:00 +0100981 if (found == 2) {
982 /* move the following values */
983 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
984 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100985 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200986 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100987 if (i == 0) {
988 found = 1;
989 } else {
990 /* this is not okay, our id is in the middle of the queue */
991 found = 2;
992 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200993 }
994 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200995 if (!found) {
996 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100997 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200998 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100999
Michal Vasko103fe632018-02-12 16:37:45 +01001000 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +01001001 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +01001002 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +01001003 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
1004 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001005}
1006
Michal Vaskof04a52a2016-04-07 10:52:10 +02001007int
Michal Vasko26043172016-07-26 14:08:59 +02001008nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001009{
1010 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001011 struct timespec ts;
1012
Michal Vasko77a6abe2017-10-05 10:02:20 +02001013 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +01001014 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001015
1016 /* LOCK */
1017 ret = pthread_mutex_timedlock(&ps->lock, &ts);
1018 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001019 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001020 return -1;
1021 }
1022
Michal Vasko74c345f2018-02-07 10:37:11 +01001023 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +01001024 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko05532772021-06-03 12:12:38 +02001025 ERR(NULL, "%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +01001026 pthread_mutex_unlock(&ps->lock);
1027 return -1;
1028 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001029
1030 /* add ourselves into the queue */
1031 nc_ps_queue_add_id(ps, id);
Michal Vasko05532772021-06-03 12:12:38 +02001032 DBL(NULL, "PS 0x%p TID %lu queue: added %u, head %u, lenght %u", ps, (long unsigned int)pthread_self(), *id,
Michal Vasko91290952019-09-27 11:30:55 +02001033 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001034
1035 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001036 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001037 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +01001038 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001039
1040 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
1041 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +05301042 /**
1043 * This may happen when another thread releases the lock and broadcasts the condition
1044 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
1045 * but when actually this thread was ready for condition.
1046 */
preetbhansali629dfc42018-12-17 16:04:40 +05301047 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +05301048 break;
1049 }
Michal Vasko66032bc2019-01-22 15:03:12 +01001050
Michal Vasko05532772021-06-03 12:12:38 +02001051 ERR(NULL, "%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001052 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001053 nc_ps_queue_remove_id(ps, *id);
1054 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001055 return -1;
1056 }
1057 }
1058
Michal Vaskobe86fe32016-04-07 10:43:03 +02001059 /* UNLOCK */
1060 pthread_mutex_unlock(&ps->lock);
1061
1062 return 0;
1063}
1064
Michal Vaskof04a52a2016-04-07 10:52:10 +02001065int
Michal Vasko26043172016-07-26 14:08:59 +02001066nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001067{
1068 int ret;
1069 struct timespec ts;
1070
Michal Vasko77a6abe2017-10-05 10:02:20 +02001071 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +01001072 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001073
1074 /* LOCK */
1075 ret = pthread_mutex_timedlock(&ps->lock, &ts);
1076 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001077 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001078 ret = -1;
1079 }
1080
Michal Vaskob30b99c2016-07-26 11:35:43 +02001081 /* we must be the first, it was our turn after all, right? */
1082 if (ps->queue[ps->queue_begin] != id) {
1083 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +02001084 /* UNLOCK */
1085 if (!ret) {
1086 pthread_mutex_unlock(&ps->lock);
1087 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001088 return -1;
1089 }
1090
Michal Vaskobe86fe32016-04-07 10:43:03 +02001091 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001092 nc_ps_queue_remove_id(ps, id);
Michal Vasko05532772021-06-03 12:12:38 +02001093 DBL(NULL, "PS 0x%p TID %lu queue: removed %u, head %u, lenght %u", ps, (long unsigned int)pthread_self(), id,
Michal Vasko91290952019-09-27 11:30:55 +02001094 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001095
1096 /* broadcast to all other threads that the queue moved */
1097 pthread_cond_broadcast(&ps->cond);
1098
Michal Vaskobe86fe32016-04-07 10:43:03 +02001099 /* UNLOCK */
1100 if (!ret) {
1101 pthread_mutex_unlock(&ps->lock);
1102 }
1103
1104 return ret;
1105}
1106
Michal Vasko428087d2016-01-14 16:04:28 +01001107API struct nc_pollsession *
1108nc_ps_new(void)
1109{
Michal Vasko48a63ed2016-03-01 09:48:21 +01001110 struct nc_pollsession *ps;
1111
1112 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +01001113 if (!ps) {
1114 ERRMEM;
1115 return NULL;
1116 }
Michal Vaskobe86fe32016-04-07 10:43:03 +02001117 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001118 pthread_mutex_init(&ps->lock, NULL);
1119
1120 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +01001121}
1122
1123API void
1124nc_ps_free(struct nc_pollsession *ps)
1125{
fanchanghu3d4e7212017-08-09 09:42:30 +08001126 uint16_t i;
1127
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001128 if (!ps) {
1129 return;
1130 }
1131
Michal Vaskobe86fe32016-04-07 10:43:03 +02001132 if (ps->queue_len) {
Michal Vasko05532772021-06-03 12:12:38 +02001133 ERR(NULL, "FATAL: Freeing a pollsession structure that is currently being worked with!");
Michal Vaskobe86fe32016-04-07 10:43:03 +02001134 }
1135
fanchanghu3d4e7212017-08-09 09:42:30 +08001136 for (i = 0; i < ps->session_count; i++) {
1137 free(ps->sessions[i]);
1138 }
1139
Michal Vasko428087d2016-01-14 16:04:28 +01001140 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001141 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001142 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001143
Michal Vasko428087d2016-01-14 16:04:28 +01001144 free(ps);
1145}
1146
1147API int
1148nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1149{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001150 uint8_t q_id;
1151
Michal Vasko45e53ae2016-04-07 11:46:03 +02001152 if (!ps) {
1153 ERRARG("ps");
1154 return -1;
1155 } else if (!session) {
1156 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001157 return -1;
1158 }
1159
Michal Vasko48a63ed2016-03-01 09:48:21 +01001160 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001161 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001162 return -1;
1163 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001164
Michal Vasko428087d2016-01-14 16:04:28 +01001165 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001166 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001167 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001168 ERRMEM;
1169 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001170 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001171 return -1;
1172 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001173 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1174 if (!ps->sessions[ps->session_count - 1]) {
1175 ERRMEM;
1176 --ps->session_count;
1177 /* UNLOCK */
1178 nc_ps_unlock(ps, q_id, __func__);
1179 return -1;
1180 }
1181 ps->sessions[ps->session_count - 1]->session = session;
1182 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001183
Michal Vasko48a63ed2016-03-01 09:48:21 +01001184 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001185 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001186}
1187
Michal Vasko48a63ed2016-03-01 09:48:21 +01001188static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001189_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001190{
1191 uint16_t i;
1192
Radek Krejcid5f978f2016-03-03 13:14:45 +01001193 if (index >= 0) {
1194 i = (uint16_t)index;
1195 goto remove;
1196 }
Michal Vasko428087d2016-01-14 16:04:28 +01001197 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001198 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001199remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001200 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001201 if (i <= ps->session_count) {
1202 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001203 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001204 }
1205 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001206 free(ps->sessions);
1207 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001208 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001209 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001210 return 0;
1211 }
1212 }
1213
Michal Vaskof0537d82016-01-29 14:42:38 +01001214 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001215}
1216
Michal Vasko48a63ed2016-03-01 09:48:21 +01001217API int
1218nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1219{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001220 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001221 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001222
Michal Vasko45e53ae2016-04-07 11:46:03 +02001223 if (!ps) {
1224 ERRARG("ps");
1225 return -1;
1226 } else if (!session) {
1227 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001228 return -1;
1229 }
1230
1231 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001232 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001233 return -1;
1234 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001235
Radek Krejcid5f978f2016-03-03 13:14:45 +01001236 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001237
1238 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001239 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001240
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001241 return ret || ret2 ? -1 : 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001242}
1243
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001244API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001245nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001246{
1247 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001248 struct nc_session *ret = NULL;
1249
1250 if (!ps) {
1251 ERRARG("ps");
1252 return NULL;
1253 }
1254
1255 /* LOCK */
1256 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1257 return NULL;
1258 }
1259
Michal Vasko4871c9d2017-10-09 14:48:39 +02001260 if (idx < ps->session_count) {
1261 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001262 }
1263
1264 /* UNLOCK */
1265 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1266
1267 return ret;
1268}
1269
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001270API uint16_t
1271nc_ps_session_count(struct nc_pollsession *ps)
1272{
Michal Vasko47003942019-03-14 12:25:23 +01001273 uint8_t q_id;
1274 uint16_t session_count;
1275
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001276 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001277 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001278 return 0;
1279 }
1280
Michal Vasko47003942019-03-14 12:25:23 +01001281 /* LOCK (just for memory barrier so that we read the current value) */
1282 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1283 return 0;
1284 }
1285
1286 session_count = ps->session_count;
1287
1288 /* UNLOCK */
1289 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1290
1291 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001292}
1293
Michal Vasko131120a2018-05-29 15:44:02 +02001294/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001295 * returns: NC_PSPOLL_ERROR,
Michal Vasko77367452021-02-16 16:32:18 +01001296 * NC_PSPOLL_TIMEOUT,
Michal Vasko71090fc2016-05-24 16:37:28 +02001297 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1298 * NC_PSPOLL_RPC
1299 */
1300static int
Michal Vasko131120a2018-05-29 15:44:02 +02001301nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001302{
Michal Vasko77367452021-02-16 16:32:18 +01001303 struct ly_in *msg;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001304 struct nc_server_reply *reply = NULL;
Michal Vasko939ffce2021-04-12 13:02:01 +02001305 struct lyd_node *e;
Michal Vasko77367452021-02-16 16:32:18 +01001306 int r, ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001307
Michal Vasko45e53ae2016-04-07 11:46:03 +02001308 if (!session) {
1309 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001310 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001311 } else if (!rpc) {
1312 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001313 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001314 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vasko05532772021-06-03 12:12:38 +02001315 ERR(session, "Invalid session to receive RPCs.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001316 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001317 }
1318
Michal Vasko93224072021-11-09 12:14:28 +01001319 *rpc = NULL;
1320
Michal Vasko77367452021-02-16 16:32:18 +01001321 /* get a message */
1322 r = nc_read_msg_io(session, io_timeout, &msg, 0);
1323 if (r == -2) {
1324 /* malformed message */
1325 ret = NC_PSPOLL_REPLY_ERROR;
Michal Vasko93224072021-11-09 12:14:28 +01001326 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vasko77367452021-02-16 16:32:18 +01001327 goto send_reply;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001328 }
1329 if (r == -1) {
Michal Vasko77367452021-02-16 16:32:18 +01001330 return NC_PSPOLL_ERROR;
1331 } else if (!r) {
1332 return NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001333 }
1334
Michal Vasko77367452021-02-16 16:32:18 +01001335 *rpc = calloc(1, sizeof **rpc);
1336 if (!*rpc) {
1337 ERRMEM;
1338 ret = NC_PSPOLL_REPLY_ERROR;
1339 goto cleanup;
1340 }
1341
1342 /* parse the RPC */
Michal Vasko93224072021-11-09 12:14:28 +01001343 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 +01001344 /* bad RPC received */
1345 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1346
1347 if ((*rpc)->envp) {
1348 /* at least the envelopes were parsed */
Michal Vasko93224072021-11-09 12:14:28 +01001349 e = nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
1350 nc_err_set_msg(e, ly_errmsg(session->ctx), "en");
Michal Vasko939ffce2021-04-12 13:02:01 +02001351 reply = nc_server_reply_err(e);
Michal Vasko77367452021-02-16 16:32:18 +01001352 } else if (session->version == NC_VERSION_11) {
Michal Vasko93224072021-11-09 12:14:28 +01001353 /* completely malformed message, NETCONF version 1.1 defines sending error reply from
1354 * the server (RFC 6241 sec. 3) */
1355 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vasko77367452021-02-16 16:32:18 +01001356 }
1357
1358send_reply:
1359 if (reply) {
Michal Vasko93224072021-11-09 12:14:28 +01001360 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, *rpc ? (*rpc)->envp : NULL, reply);
Michal Vasko77367452021-02-16 16:32:18 +01001361 nc_server_reply_free(reply);
1362 if (r != NC_MSG_REPLY) {
Michal Vasko05532772021-06-03 12:12:38 +02001363 ERR(session, "Failed to write reply (%s), terminating session.", nc_msgtype2str[r]);
Michal Vasko77367452021-02-16 16:32:18 +01001364 if (session->status != NC_STATUS_INVALID) {
1365 session->status = NC_STATUS_INVALID;
1366 session->term_reason = NC_SESSION_TERM_OTHER;
1367 }
1368 }
1369 }
1370 } else {
1371 ret = NC_PSPOLL_RPC;
1372 }
1373
1374cleanup:
1375 ly_in_free(msg, 1);
1376 if (ret != NC_PSPOLL_RPC) {
1377 nc_server_rpc_free(*rpc);
1378 *rpc = NULL;
1379 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001380 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001381}
1382
fanchanghu966f2de2016-07-21 02:28:57 -04001383API void
1384nc_set_global_rpc_clb(nc_rpc_clb clb)
1385{
1386 global_rpc_clb = clb;
1387}
1388
Radek Krejci93e80222016-10-03 13:34:25 +02001389API NC_MSG_TYPE
1390nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1391{
Michal Vasko131120a2018-05-29 15:44:02 +02001392 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001393
1394 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001395 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001396 ERRARG("session");
1397 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001398 } else if (!notif || !notif->ntf || !notif->eventtime) {
Radek Krejci93e80222016-10-03 13:34:25 +02001399 ERRARG("notif");
1400 return NC_MSG_ERROR;
1401 }
1402
Michal Vasko131120a2018-05-29 15:44:02 +02001403 /* we do not need RPC lock for this, IO lock will be acquired properly */
1404 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001405 if (ret != NC_MSG_NOTIF) {
Michal Vasko05532772021-06-03 12:12:38 +02001406 ERR(session, "Failed to write notification (%s).", nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001407 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001408
Michal Vasko131120a2018-05-29 15:44:02 +02001409 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001410}
1411
Michal Vasko131120a2018-05-29 15:44:02 +02001412/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001413 * returns: NC_PSPOLL_ERROR,
1414 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1415 * NC_PSPOLL_REPLY_ERROR,
1416 * 0
1417 */
1418static int
Michal Vasko93224072021-11-09 12:14:28 +01001419nc_server_send_reply_io(struct nc_session *session, int io_timeout, const struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001420{
1421 nc_rpc_clb clb;
1422 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +01001423 const struct lysc_node *rpc_act = NULL;
1424 struct lyd_node *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001425 int ret = 0;
1426 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001427
Michal Vasko4a827e52016-03-03 10:59:00 +01001428 if (!rpc) {
1429 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001430 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001431 }
1432
Michal Vasko77367452021-02-16 16:32:18 +01001433 if (rpc->rpc->schema->nodetype == LYS_RPC) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001434 /* RPC */
Michal Vasko77367452021-02-16 16:32:18 +01001435 rpc_act = rpc->rpc->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001436 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001437 /* action */
Michal Vasko77367452021-02-16 16:32:18 +01001438 LYD_TREE_DFS_BEGIN(rpc->rpc, elem) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001439 if (elem->schema->nodetype == LYS_ACTION) {
1440 rpc_act = elem->schema;
1441 break;
1442 }
Michal Vasko77367452021-02-16 16:32:18 +01001443 LYD_TREE_DFS_END(rpc->rpc, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001444 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001445 if (!rpc_act) {
1446 ERRINT;
1447 return NC_PSPOLL_ERROR;
1448 }
1449 }
1450
1451 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001452 if (!global_rpc_clb) {
1453 /* no callback, reply with a not-implemented error */
Michal Vasko93224072021-11-09 12:14:28 +01001454 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 +03001455 } else {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001456 reply = global_rpc_clb(rpc->rpc, session);
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001457 }
Michal Vasko428087d2016-01-14 16:04:28 +01001458 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001459 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko77367452021-02-16 16:32:18 +01001460 reply = clb(rpc->rpc, session);
Michal Vasko428087d2016-01-14 16:04:28 +01001461 }
1462
1463 if (!reply) {
Michal Vasko93224072021-11-09 12:14:28 +01001464 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001465 }
Michal Vasko77367452021-02-16 16:32:18 +01001466 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->envp, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001467 if (reply->type == NC_RPL_ERROR) {
1468 ret |= NC_PSPOLL_REPLY_ERROR;
1469 }
1470 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001471
Michal Vasko131120a2018-05-29 15:44:02 +02001472 if (r != NC_MSG_REPLY) {
Michal Vasko15469492021-06-09 08:40:48 +02001473 ERR(session, "Failed to write reply (%s).", nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001474 ret |= NC_PSPOLL_ERROR;
1475 }
Michal Vasko428087d2016-01-14 16:04:28 +01001476
1477 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1478 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1479 session->status = NC_STATUS_INVALID;
1480 }
1481
Michal Vasko71090fc2016-05-24 16:37:28 +02001482 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001483}
1484
Michal Vasko131120a2018-05-29 15:44:02 +02001485/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001486 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1487 * NC_PSPOLL_ERROR, (msg filled)
1488 * NC_PSPOLL_TIMEOUT,
1489 * NC_PSPOLL_RPC (some application data available),
1490 * NC_PSPOLL_SSH_CHANNEL,
1491 * NC_PSPOLL_SSH_MSG
1492 */
1493static int
Michal Vasko131120a2018-05-29 15:44:02 +02001494nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001495{
Michal Vasko9a327362017-01-11 11:31:46 +01001496 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001497 int r, ret = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001498
Michal Vasko9a327362017-01-11 11:31:46 +01001499#ifdef NC_ENABLED_SSH
1500 struct nc_session *new;
1501#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001502
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001503 /* check timeout first */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001504 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout &&
1505 (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001506 sprintf(msg, "session idle timeout elapsed");
1507 session->status = NC_STATUS_INVALID;
1508 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1509 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1510 }
1511
Michal Vasko131120a2018-05-29 15:44:02 +02001512 r = nc_session_io_lock(session, io_timeout, __func__);
1513 if (r < 0) {
1514 sprintf(msg, "session IO lock failed to be acquired");
1515 return NC_PSPOLL_ERROR;
1516 } else if (!r) {
1517 return NC_PSPOLL_TIMEOUT;
1518 }
1519
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001520 switch (session->ti_type) {
1521#ifdef NC_ENABLED_SSH
1522 case NC_TI_LIBSSH:
1523 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001524 if (r == SSH_EOF) {
1525 sprintf(msg, "SSH channel unexpected EOF");
1526 session->status = NC_STATUS_INVALID;
1527 session->term_reason = NC_SESSION_TERM_DROPPED;
1528 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1529 } else if (r == SSH_ERROR) {
1530 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001531 session->status = NC_STATUS_INVALID;
1532 session->term_reason = NC_SESSION_TERM_OTHER;
1533 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001534 } else if (!r) {
1535 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1536 /* new SSH message */
1537 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1538 if (session->ti.libssh.next) {
1539 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001540 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel &&
1541 (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko8dcaa882017-10-19 14:28:42 +02001542 /* new NETCONF SSH channel */
1543 ret = NC_PSPOLL_SSH_CHANNEL;
1544 break;
1545 }
1546 }
1547 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001548 break;
1549 }
1550 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001551
Michal Vasko8dcaa882017-10-19 14:28:42 +02001552 /* just some SSH message */
1553 ret = NC_PSPOLL_SSH_MSG;
1554 } else {
1555 ret = NC_PSPOLL_TIMEOUT;
1556 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001557 } else {
1558 /* we have some application data */
1559 ret = NC_PSPOLL_RPC;
1560 }
1561 break;
1562#endif
1563#ifdef NC_ENABLED_TLS
1564 case NC_TI_OPENSSL:
1565 r = SSL_pending(session->ti.tls);
1566 if (!r) {
1567 /* no data pending in the SSL buffer, poll fd */
1568 pfd.fd = SSL_get_rfd(session->ti.tls);
1569 if (pfd.fd < 0) {
1570 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1571 ret = NC_PSPOLL_ERROR;
1572 break;
1573 }
1574 pfd.events = POLLIN;
1575 pfd.revents = 0;
1576 r = poll(&pfd, 1, 0);
1577
1578 if ((r < 0) && (errno != EINTR)) {
1579 sprintf(msg, "poll failed (%s)", strerror(errno));
1580 session->status = NC_STATUS_INVALID;
1581 ret = NC_PSPOLL_ERROR;
1582 } else if (r > 0) {
1583 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1584 sprintf(msg, "communication socket unexpectedly closed");
1585 session->status = NC_STATUS_INVALID;
1586 session->term_reason = NC_SESSION_TERM_DROPPED;
1587 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1588 } else if (pfd.revents & POLLERR) {
1589 sprintf(msg, "communication socket error");
1590 session->status = NC_STATUS_INVALID;
1591 session->term_reason = NC_SESSION_TERM_OTHER;
1592 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1593 } else {
1594 ret = NC_PSPOLL_RPC;
1595 }
1596 } else {
1597 ret = NC_PSPOLL_TIMEOUT;
1598 }
1599 } else {
1600 ret = NC_PSPOLL_RPC;
1601 }
1602 break;
1603#endif
1604 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001605 case NC_TI_UNIX:
1606 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001607 pfd.events = POLLIN;
1608 pfd.revents = 0;
1609 r = poll(&pfd, 1, 0);
1610
1611 if ((r < 0) && (errno != EINTR)) {
1612 sprintf(msg, "poll failed (%s)", strerror(errno));
1613 session->status = NC_STATUS_INVALID;
1614 ret = NC_PSPOLL_ERROR;
1615 } else if (r > 0) {
1616 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1617 sprintf(msg, "communication socket unexpectedly closed");
1618 session->status = NC_STATUS_INVALID;
1619 session->term_reason = NC_SESSION_TERM_DROPPED;
1620 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1621 } else if (pfd.revents & POLLERR) {
1622 sprintf(msg, "communication socket error");
1623 session->status = NC_STATUS_INVALID;
1624 session->term_reason = NC_SESSION_TERM_OTHER;
1625 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1626 } else {
1627 ret = NC_PSPOLL_RPC;
1628 }
1629 } else {
1630 ret = NC_PSPOLL_TIMEOUT;
1631 }
1632 break;
1633 case NC_TI_NONE:
1634 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1635 ret = NC_PSPOLL_ERROR;
1636 break;
1637 }
1638
Michal Vasko131120a2018-05-29 15:44:02 +02001639 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001640 return ret;
1641}
1642
1643API int
1644nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1645{
1646 int ret, r;
1647 uint8_t q_id;
1648 uint16_t i, j;
1649 char msg[256];
1650 struct timespec ts_timeout, ts_cur;
1651 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001652 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001653 struct nc_server_rpc *rpc = NULL;
1654
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001655 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001656 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001657 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001658 }
1659
Michal Vaskoade892d2017-02-22 13:40:35 +01001660 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001661 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001662 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001663 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001664
Michal Vaskoade892d2017-02-22 13:40:35 +01001665 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001666 nc_ps_unlock(ps, q_id, __func__);
1667 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001668 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001669
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001670 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001671 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001672 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001673 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001674 nc_addtimespec(&ts_timeout, timeout);
1675 }
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 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001768 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001769
1770 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1771 /* final timeout */
1772 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001773 }
Michal Vasko428087d2016-01-14 16:04:28 +01001774 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001775 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001776
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001777 /* do we want to return the session? */
1778 switch (ret) {
1779 case NC_PSPOLL_RPC:
1780 case NC_PSPOLL_SESSION_TERM:
1781 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1782#ifdef NC_ENABLED_SSH
1783 case NC_PSPOLL_SSH_CHANNEL:
1784 case NC_PSPOLL_SSH_MSG:
1785#endif
1786 if (session) {
1787 *session = cur_session;
1788 }
1789 ps->last_event_session = i;
1790 break;
1791 default:
1792 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001793 }
Michal Vasko428087d2016-01-14 16:04:28 +01001794
Michal Vaskoade892d2017-02-22 13:40:35 +01001795 /* PS UNLOCK */
1796 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001797
Michal Vasko131120a2018-05-29 15:44:02 +02001798 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001799 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001800 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001801 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1802 if (cur_session->status != NC_STATUS_RUNNING) {
1803 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001804 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001805 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001806 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001807 }
1808 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001809 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001810
Michal Vasko7f1ee932018-10-11 09:41:42 +02001811 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001812 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001813 if (cur_session->status != NC_STATUS_RUNNING) {
1814 ret |= NC_PSPOLL_SESSION_TERM;
1815 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1816 ret |= NC_PSPOLL_SESSION_ERROR;
1817 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001818 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001819 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001820 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001821 }
Michal Vasko428087d2016-01-14 16:04:28 +01001822 }
Michal Vasko77367452021-02-16 16:32:18 +01001823 nc_server_rpc_free(rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001824
Michal Vasko131120a2018-05-29 15:44:02 +02001825 /* SESSION RPC UNLOCK */
1826 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001827 }
1828
Michal Vasko48a63ed2016-03-01 09:48:21 +01001829 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001830}
1831
Michal Vaskod09eae62016-02-01 10:32:52 +01001832API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001833nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001834{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001835 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001836 uint16_t i;
1837 struct nc_session *session;
1838
Michal Vasko9a25e932016-02-01 10:36:42 +01001839 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001840 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001841 return;
1842 }
1843
Michal Vasko48a63ed2016-03-01 09:48:21 +01001844 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001845 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001846 return;
1847 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001848
Michal Vasko48a63ed2016-03-01 09:48:21 +01001849 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001850 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001851 nc_session_free(ps->sessions[i]->session, data_free);
1852 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001853 }
1854 free(ps->sessions);
1855 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001856 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001857 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001858 } else {
1859 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001860 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1861 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001862 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001863 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001864 continue;
1865 }
1866
1867 ++i;
1868 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001869 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001870
1871 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001872 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001873}
1874
Michal Vasko5f352c52019-07-10 16:12:06 +02001875static int
apropp-molex4e903c32020-04-20 03:06:58 -04001876nc_get_uid(int sock, uid_t *uid)
1877{
Michal Vaskod3910912020-04-20 09:12:49 +02001878 int ret;
apropp-molex4e903c32020-04-20 03:06:58 -04001879
Michal Vaskod3910912020-04-20 09:12:49 +02001880#ifdef SO_PEERCRED
1881 struct ucred ucred;
1882 socklen_t len;
1883 len = sizeof(ucred);
1884 ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
1885 if (!ret) {
1886 *uid = ucred.uid;
1887 }
1888#else
1889 ret = getpeereid(sock, uid, NULL);
1890#endif
1891
1892 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001893 ERR(NULL, "Failed to get credentials from unix socket (%s).", strerror(errno));
Michal Vaskod3910912020-04-20 09:12:49 +02001894 return -1;
1895 }
apropp-molex4e903c32020-04-20 03:06:58 -04001896 return 0;
1897}
1898
1899static int
Michal Vasko5f352c52019-07-10 16:12:06 +02001900nc_accept_unix(struct nc_session *session, int sock)
1901{
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001902#if defined (SO_PEERCRED) || defined (HAVE_GETPEEREID)
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001903 struct passwd *pw, pw_buf;
Michal Vasko5f352c52019-07-10 16:12:06 +02001904 char *username;
Michal Vasko5f352c52019-07-10 16:12:06 +02001905 session->ti_type = NC_TI_UNIX;
Michal Vasko143aa142021-10-01 15:31:48 +02001906 uid_t uid = 0;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001907 char *buf = NULL;
1908 size_t buf_len = 0;
Michal Vasko5f352c52019-07-10 16:12:06 +02001909
Michal Vaskod3910912020-04-20 09:12:49 +02001910 if (nc_get_uid(sock, &uid)) {
1911 close(sock);
Michal Vasko5f352c52019-07-10 16:12:06 +02001912 return -1;
1913 }
1914
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001915 pw = nc_getpwuid(uid, &pw_buf, &buf, &buf_len);
Michal Vasko5f352c52019-07-10 16:12:06 +02001916 if (pw == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001917 ERR(NULL, "Failed to find username for uid=%u (%s).\n", uid, strerror(errno));
Michal Vasko5f352c52019-07-10 16:12:06 +02001918 close(sock);
1919 return -1;
1920 }
1921
1922 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001923 free(buf);
Michal Vasko5f352c52019-07-10 16:12:06 +02001924 if (username == NULL) {
1925 ERRMEM;
1926 close(sock);
1927 return -1;
1928 }
Michal Vasko93224072021-11-09 12:14:28 +01001929 session->username = username;
Michal Vasko5f352c52019-07-10 16:12:06 +02001930
1931 session->ti.unixsock.sock = sock;
1932
1933 return 1;
Claus Klein22091912020-01-20 13:45:47 +01001934#else
1935 return -1;
1936#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02001937}
1938
Michal Vaskoe2713da2016-08-22 16:06:40 +02001939API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001940nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001941{
Michal Vasko3031aae2016-01-27 16:07:18 +01001942 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001943 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001944
Michal Vasko45e53ae2016-04-07 11:46:03 +02001945 if (!name) {
1946 ERRARG("name");
1947 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001948 }
1949
Michal Vaskoade892d2017-02-22 13:40:35 +01001950 /* BIND LOCK */
1951 pthread_mutex_lock(&server_opts.bind_lock);
1952
1953 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001954 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001955
1956 /* check name uniqueness */
1957 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001958 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02001959 ERR(NULL, "Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001960 ret = -1;
1961 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001962 }
1963 }
1964
Michal Vasko93224072021-11-09 12:14:28 +01001965 server_opts.endpts = nc_realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001966 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001967 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001968 ret = -1;
1969 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001970 }
Michal Vasko93224072021-11-09 12:14:28 +01001971 memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts);
1972 ++server_opts.endpt_count;
1973
1974 server_opts.endpts[server_opts.endpt_count - 1].name = strdup(name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001975 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001976 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
1977 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
1978 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001979
Michal Vaskoe2713da2016-08-22 16:06:40 +02001980 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001981 if (!server_opts.binds) {
1982 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001983 ret = -1;
1984 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001985 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001986
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001987 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001988 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1989
1990 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001991#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001992 case NC_TI_LIBSSH:
1993 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1994 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1995 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001996 ret = -1;
1997 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001998 }
1999 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
Michal Vasko77367452021-02-16 16:32:18 +01002000 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002001 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002002 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002003 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002004#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02002005#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002006 case NC_TI_OPENSSL:
2007 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2008 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
2009 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002010 ret = -1;
2011 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002012 }
2013 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002014#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002015 case NC_TI_UNIX:
2016 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
2017 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
2018 ERRMEM;
2019 ret = -1;
2020 goto cleanup;
2021 }
2022 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
2023 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
2024 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
2025 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002026 default:
2027 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01002028 ret = -1;
2029 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002030 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002031
Michal Vaskoade892d2017-02-22 13:40:35 +01002032cleanup:
2033 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002034 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002035
Michal Vaskoade892d2017-02-22 13:40:35 +01002036 /* BIND UNLOCK */
2037 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002038
Michal Vaskoade892d2017-02-22 13:40:35 +01002039 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002040}
2041
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002042API int
Michal Vasko59050372016-11-22 14:33:55 +01002043nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01002044{
2045 uint32_t i;
2046 int ret = -1;
2047
Michal Vaskoade892d2017-02-22 13:40:35 +01002048 /* BIND LOCK */
2049 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002050
Michal Vaskoade892d2017-02-22 13:40:35 +01002051 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002052 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002053
Michal Vasko59050372016-11-22 14:33:55 +01002054 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002055 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01002056 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002057 free(server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002058 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002059#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002060 case NC_TI_LIBSSH:
2061 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2062 free(server_opts.endpts[i].opts.ssh);
2063 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002064#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002065#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002066 case NC_TI_OPENSSL:
2067 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2068 free(server_opts.endpts[i].opts.tls);
2069 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002070#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002071 case NC_TI_UNIX:
2072 free(server_opts.endpts[i].opts.unixsock);
2073 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002074 default:
2075 ERRINT;
2076 /* won't get here ...*/
2077 break;
2078 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002079 ret = 0;
2080 }
Michal Vasko3031aae2016-01-27 16:07:18 +01002081 free(server_opts.endpts);
2082 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002083
2084 /* remove all binds */
2085 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002086 free(server_opts.binds[i].address);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002087 if (server_opts.binds[i].sock > -1) {
2088 close(server_opts.binds[i].sock);
2089 }
2090 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002091 free(server_opts.binds);
2092 server_opts.binds = NULL;
2093
Michal Vasko3031aae2016-01-27 16:07:18 +01002094 server_opts.endpt_count = 0;
2095
Michal Vasko1a38c862016-01-15 15:50:07 +01002096 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002097 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002098 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002099 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002100 /* remove endpt */
Michal Vasko93224072021-11-09 12:14:28 +01002101 free(server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002102 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002103#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002104 case NC_TI_LIBSSH:
2105 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2106 free(server_opts.endpts[i].opts.ssh);
2107 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002108#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002109#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002110 case NC_TI_OPENSSL:
2111 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2112 free(server_opts.endpts[i].opts.tls);
2113 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002114#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002115 case NC_TI_UNIX:
2116 free(server_opts.endpts[i].opts.unixsock);
2117 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002118 default:
2119 ERRINT;
2120 break;
2121 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002122
Michal Vaskoe2713da2016-08-22 16:06:40 +02002123 /* remove bind(s) */
Michal Vasko93224072021-11-09 12:14:28 +01002124 free(server_opts.binds[i].address);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002125 if (server_opts.binds[i].sock > -1) {
2126 close(server_opts.binds[i].sock);
2127 }
2128
2129 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002130 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002131 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002132 free(server_opts.binds);
2133 server_opts.binds = NULL;
2134 free(server_opts.endpts);
2135 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002136 } else if (i < server_opts.endpt_count) {
2137 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2138 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002139 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002140
2141 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002142 if (name) {
2143 break;
2144 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002145 }
2146 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002147 }
2148
Michal Vaskoade892d2017-02-22 13:40:35 +01002149 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002150 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002151
Michal Vaskoade892d2017-02-22 13:40:35 +01002152 /* BIND UNLOCK */
2153 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002154
2155 return ret;
2156}
2157
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002158API int
2159nc_server_endpt_count(void)
2160{
2161 return server_opts.endpt_count;
2162}
2163
Michal Vasko1b5973e2020-01-30 16:05:46 +01002164API int
2165nc_server_is_endpt(const char *name)
2166{
2167 uint16_t i;
2168 int found = 0;
2169
Michal Vaskofb1724b2020-01-31 11:02:00 +01002170 if (!name) {
2171 return found;
2172 }
2173
Michal Vasko1b5973e2020-01-30 16:05:46 +01002174 /* ENDPT READ LOCK */
2175 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2176
2177 /* check name uniqueness */
2178 for (i = 0; i < server_opts.endpt_count; ++i) {
2179 if (!strcmp(server_opts.endpts[i].name, name)) {
2180 found = 1;
2181 break;
2182 }
2183 }
2184
2185 /* ENDPT UNLOCK */
2186 pthread_rwlock_unlock(&server_opts.endpt_lock);
2187
2188 return found;
2189}
2190
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002191int
2192nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
2193{
2194 struct nc_endpt *endpt;
2195 struct nc_bind *bind = NULL;
2196 uint16_t i;
2197 int sock = -1, set_addr, ret = 0;
2198
2199 if (!endpt_name) {
2200 ERRARG("endpt_name");
2201 return -1;
2202 } else if ((!address && !port) || (address && port)) {
2203 ERRARG("address and port");
2204 return -1;
2205 }
2206
2207 if (address) {
2208 set_addr = 1;
2209 } else {
2210 set_addr = 0;
2211 }
2212
2213 /* BIND LOCK */
2214 pthread_mutex_lock(&server_opts.bind_lock);
2215
2216 /* ENDPT LOCK */
2217 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2218 if (!endpt) {
2219 /* BIND UNLOCK */
2220 pthread_mutex_unlock(&server_opts.bind_lock);
2221 return -1;
2222 }
2223
2224 bind = &server_opts.binds[i];
2225
2226 if (set_addr) {
2227 port = bind->port;
2228 } else {
2229 address = bind->address;
2230 }
2231
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002232 if (!set_addr && (endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002233 ret = -1;
2234 goto cleanup;
2235 }
2236
2237 /* we have all the information we need to create a listening socket */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002238 if (address && (port || (endpt->ti == NC_TI_UNIX))) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002239 /* create new socket, close the old one */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002240 if (endpt->ti == NC_TI_UNIX) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002241 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002242 } else {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002243 sock = nc_sock_listen_inet(address, port, &endpt->ka);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002244 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002245 if (sock == -1) {
2246 ret = -1;
2247 goto cleanup;
2248 }
2249
2250 if (bind->sock > -1) {
2251 close(bind->sock);
2252 }
2253 bind->sock = sock;
2254 } /* else we are just setting address or port */
2255
2256 if (set_addr) {
Michal Vasko93224072021-11-09 12:14:28 +01002257 free(bind->address);
2258 bind->address = strdup(address);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002259 } else {
2260 bind->port = port;
2261 }
2262
2263 if (sock > -1) {
Michal Vasko946cacb2020-08-12 11:18:08 +02002264 switch (endpt->ti) {
2265 case NC_TI_UNIX:
Michal Vasko05532772021-06-03 12:12:38 +02002266 VRB(NULL, "Listening on %s for UNIX connections.", address);
Michal Vasko946cacb2020-08-12 11:18:08 +02002267 break;
2268#ifdef NC_ENABLED_SSH
2269 case NC_TI_LIBSSH:
Michal Vasko05532772021-06-03 12:12:38 +02002270 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002271 break;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002272#endif
Michal Vasko946cacb2020-08-12 11:18:08 +02002273#ifdef NC_ENABLED_TLS
2274 case NC_TI_OPENSSL:
Michal Vasko05532772021-06-03 12:12:38 +02002275 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002276 break;
2277#endif
2278 default:
2279 ERRINT;
2280 break;
2281 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002282 }
2283
2284cleanup:
2285 /* ENDPT UNLOCK */
2286 pthread_rwlock_unlock(&server_opts.endpt_lock);
2287
2288 /* BIND UNLOCK */
2289 pthread_mutex_unlock(&server_opts.bind_lock);
2290
2291 return ret;
2292}
2293
2294API int
2295nc_server_endpt_set_address(const char *endpt_name, const char *address)
2296{
2297 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2298}
2299
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002300#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002301
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002302API int
2303nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2304{
2305 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2306}
2307
Michal Vasko946cacb2020-08-12 11:18:08 +02002308#endif
2309
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002310API int
2311nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2312{
2313 struct nc_endpt *endpt;
2314 uint16_t i;
2315 int ret = 0;
2316
2317 if (!endpt_name) {
2318 ERRARG("endpt_name");
2319 return -1;
2320 } else if (mode == 0) {
2321 ERRARG("mode");
2322 return -1;
2323 }
2324
2325 /* ENDPT LOCK */
2326 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002327 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002328 return -1;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002329 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002330
2331 if (endpt->ti != NC_TI_UNIX) {
2332 ret = -1;
2333 goto cleanup;
2334 }
2335
2336 endpt->opts.unixsock->mode = mode;
2337 endpt->opts.unixsock->uid = uid;
2338 endpt->opts.unixsock->gid = gid;
2339
2340cleanup:
2341 /* ENDPT UNLOCK */
2342 pthread_rwlock_unlock(&server_opts.endpt_lock);
2343
2344 return ret;
2345}
2346
2347API int
2348nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2349{
2350 struct nc_endpt *endpt;
2351 int ret = 0;
2352
2353 if (!endpt_name) {
2354 ERRARG("endpt_name");
2355 return -1;
2356 }
2357
2358 /* ENDPT LOCK */
2359 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2360 if (!endpt) {
2361 return -1;
2362 }
2363
2364 endpt->ka.enabled = (enable ? 1 : 0);
2365
2366 /* ENDPT UNLOCK */
2367 pthread_rwlock_unlock(&server_opts.endpt_lock);
2368
2369 return ret;
2370}
2371
2372API int
2373nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2374{
2375 struct nc_endpt *endpt;
2376 int ret = 0;
2377
2378 if (!endpt_name) {
2379 ERRARG("endpt_name");
2380 return -1;
2381 }
2382
2383 /* ENDPT LOCK */
2384 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2385 if (!endpt) {
2386 return -1;
2387 }
2388
2389 if (idle_time > -1) {
2390 endpt->ka.idle_time = idle_time;
2391 }
2392 if (max_probes > -1) {
2393 endpt->ka.max_probes = max_probes;
2394 }
2395 if (probe_interval > -1) {
2396 endpt->ka.probe_interval = probe_interval;
2397 }
2398
2399 /* ENDPT UNLOCK */
2400 pthread_rwlock_unlock(&server_opts.endpt_lock);
2401
2402 return ret;
2403}
2404
Michal Vasko71090fc2016-05-24 16:37:28 +02002405API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +01002406nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002407{
Michal Vasko71090fc2016-05-24 16:37:28 +02002408 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002409 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002410 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002411 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002412 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002413
Michal Vasko93224072021-11-09 12:14:28 +01002414 if (!ctx) {
2415 ERRARG("ctx");
Michal Vasko71090fc2016-05-24 16:37:28 +02002416 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002417 } else if (!session) {
2418 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002419 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002420 }
2421
Michal Vasko93224072021-11-09 12:14:28 +01002422 /* init ctx as needed */
2423 nc_server_init_ctx(ctx);
2424
Michal Vaskoade892d2017-02-22 13:40:35 +01002425 /* BIND LOCK */
2426 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002427
2428 if (!server_opts.endpt_count) {
Michal Vasko05532772021-06-03 12:12:38 +02002429 ERR(NULL, "No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002430 /* BIND UNLOCK */
2431 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002432 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002433 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002434
Michal Vaskoe2713da2016-08-22 16:06:40 +02002435 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002436 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002437 /* BIND UNLOCK */
2438 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002439 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002440 if (!ret) {
2441 return NC_MSG_WOULDBLOCK;
2442 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002443 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002444 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002445
2446 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2447 /* ENDPT READ LOCK */
2448 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2449
2450 /* BIND UNLOCK */
2451 pthread_mutex_unlock(&server_opts.bind_lock);
2452
Michal Vaskob48aa812016-01-18 14:13:09 +01002453 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002454
Michal Vasko131120a2018-05-29 15:44:02 +02002455 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002456 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002457 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002458 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002459 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002460 msgtype = NC_MSG_ERROR;
2461 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002462 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002463 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01002464 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko1a38c862016-01-15 15:50:07 +01002465 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +01002466 (*session)->host = host;
Michal Vasko1a38c862016-01-15 15:50:07 +01002467 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002468
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002469 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002470#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002471 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2472 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002473 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002474 if (ret < 0) {
2475 msgtype = NC_MSG_ERROR;
2476 goto cleanup;
2477 } else if (!ret) {
2478 msgtype = NC_MSG_WOULDBLOCK;
2479 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002480 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002481 } else
2482#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002483#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002484 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2485 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002486 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002487 if (ret < 0) {
2488 msgtype = NC_MSG_ERROR;
2489 goto cleanup;
2490 } else if (!ret) {
2491 msgtype = NC_MSG_WOULDBLOCK;
2492 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002493 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002494 } else
2495#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002496 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2497 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2498 ret = nc_accept_unix(*session, sock);
2499 if (ret < 0) {
2500 msgtype = NC_MSG_ERROR;
2501 goto cleanup;
2502 }
2503 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002504 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002505 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002506 msgtype = NC_MSG_ERROR;
2507 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002508 }
2509
Michal Vasko2cc4c682016-03-01 09:16:48 +01002510 (*session)->data = NULL;
2511
Michal Vaskoade892d2017-02-22 13:40:35 +01002512 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002513 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002514
Michal Vaskob48aa812016-01-18 14:13:09 +01002515 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002516 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002517
Michal Vasko9e036d52016-01-08 10:49:26 +01002518 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002519 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002520 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002521 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002522 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002523 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002524 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002525
2526 nc_gettimespec_mono(&ts_cur);
2527 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2528 nc_gettimespec_real(&ts_cur);
2529 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002530 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002531
Michal Vasko71090fc2016-05-24 16:37:28 +02002532 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002533
Michal Vasko71090fc2016-05-24 16:37:28 +02002534cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002535 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002536 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002537
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002538 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002539 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002540 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002541}
2542
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002543#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002544
Michal Vaskoadf30f02019-06-24 09:34:47 +02002545/* client is expected to be locked */
2546static int
2547_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 +02002548{
2549 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002550 int ret = -1;
2551
2552 if (!endpt_name) {
2553 /* remove all endpoints */
2554 for (i = 0; i < client->ch_endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002555 free(client->ch_endpts[i].name);
2556 free(client->ch_endpts[i].address);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002557 if (client->ch_endpts[i].sock_pending != -1) {
2558 close(client->ch_endpts[i].sock_pending);
2559 }
2560 switch (client->ch_endpts[i].ti) {
2561#ifdef NC_ENABLED_SSH
2562 case NC_TI_LIBSSH:
2563 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2564 free(client->ch_endpts[i].opts.ssh);
2565 break;
2566#endif
2567#ifdef NC_ENABLED_TLS
2568 case NC_TI_OPENSSL:
2569 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2570 free(client->ch_endpts[i].opts.tls);
2571 break;
2572#endif
2573 default:
2574 ERRINT;
2575 /* won't get here ...*/
2576 break;
2577 }
2578 }
2579 free(client->ch_endpts);
2580 client->ch_endpts = NULL;
2581 client->ch_endpt_count = 0;
2582
2583 ret = 0;
2584 } else {
2585 for (i = 0; i < client->ch_endpt_count; ++i) {
2586 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
Michal Vasko93224072021-11-09 12:14:28 +01002587 free(client->ch_endpts[i].name);
2588 free(client->ch_endpts[i].address);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002589 if (client->ch_endpts[i].sock_pending != -1) {
2590 close(client->ch_endpts[i].sock_pending);
2591 }
2592 switch (client->ch_endpts[i].ti) {
2593#ifdef NC_ENABLED_SSH
2594 case NC_TI_LIBSSH:
2595 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2596 free(client->ch_endpts[i].opts.ssh);
2597 break;
2598#endif
2599#ifdef NC_ENABLED_TLS
2600 case NC_TI_OPENSSL:
2601 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2602 free(client->ch_endpts[i].opts.tls);
2603 break;
2604#endif
2605 default:
2606 ERRINT;
2607 /* won't get here ...*/
2608 break;
2609 }
2610
2611 /* move last endpoint to the empty space */
2612 --client->ch_endpt_count;
2613 if (i < client->ch_endpt_count) {
2614 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2615 } else if (!server_opts.ch_client_count) {
2616 free(server_opts.ch_clients);
2617 server_opts.ch_clients = NULL;
2618 }
2619
2620 ret = 0;
2621 break;
2622 }
2623 }
2624 }
2625
2626 return ret;
2627}
2628
2629API int
2630nc_server_ch_add_client(const char *name)
2631{
2632 uint16_t i;
2633 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002634
2635 if (!name) {
2636 ERRARG("name");
2637 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002638 }
2639
2640 /* WRITE LOCK */
2641 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2642
2643 /* check name uniqueness */
2644 for (i = 0; i < server_opts.ch_client_count; ++i) {
2645 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002646 ERR(NULL, "Call Home client \"%s\" already exists.", name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002647 /* WRITE UNLOCK */
2648 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2649 return -1;
2650 }
2651 }
2652
2653 ++server_opts.ch_client_count;
2654 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2655 if (!server_opts.ch_clients) {
2656 ERRMEM;
2657 /* WRITE UNLOCK */
2658 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2659 return -1;
2660 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002661 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002662
Michal Vasko93224072021-11-09 12:14:28 +01002663 client->name = strdup(name);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002664 client->id = ATOMIC_INC_RELAXED(server_opts.new_client_id);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002665 client->ch_endpts = NULL;
2666 client->ch_endpt_count = 0;
2667 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002668
Michal Vasko2e6defd2016-10-07 15:48:15 +02002669 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002670 client->start_with = NC_CH_FIRST_LISTED;
2671 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002672
Michal Vaskoadf30f02019-06-24 09:34:47 +02002673 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002674
2675 /* WRITE UNLOCK */
2676 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2677
2678 return 0;
2679}
2680
2681API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002682nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002683{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002684 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002685 int ret = -1;
2686
2687 /* WRITE LOCK */
2688 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2689
Michal Vaskoadf30f02019-06-24 09:34:47 +02002690 if (!name) {
2691 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002692 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002693 free(server_opts.ch_clients[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002694
2695 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002696 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002697
2698 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002699 ret = 0;
2700 }
2701 free(server_opts.ch_clients);
2702 server_opts.ch_clients = NULL;
2703
2704 server_opts.ch_client_count = 0;
2705
2706 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002707 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002708 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002709 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko93224072021-11-09 12:14:28 +01002710 free(server_opts.ch_clients[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002711
Michal Vasko2e6defd2016-10-07 15:48:15 +02002712 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002713 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002714
2715 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2716
2717 /* move last client and endpoint(s) to the empty space */
2718 --server_opts.ch_client_count;
2719 if (i < server_opts.ch_client_count) {
2720 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002721 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002722 } else if (!server_opts.ch_client_count) {
2723 free(server_opts.ch_clients);
2724 server_opts.ch_clients = NULL;
2725 }
2726
2727 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002728 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002729 }
2730 }
2731 }
2732
2733 /* WRITE UNLOCK */
2734 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2735
2736 return ret;
2737}
2738
2739API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002740nc_server_ch_is_client(const char *name)
2741{
2742 uint16_t i;
2743 int found = 0;
2744
2745 if (!name) {
2746 return found;
2747 }
2748
2749 /* READ LOCK */
2750 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2751
2752 /* check name uniqueness */
2753 for (i = 0; i < server_opts.ch_client_count; ++i) {
2754 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2755 found = 1;
2756 break;
2757 }
2758 }
2759
2760 /* UNLOCK */
2761 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2762
2763 return found;
2764}
2765
2766API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002767nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002768{
2769 uint16_t i;
2770 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002771 struct nc_ch_endpt *endpt;
2772 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002773
2774 if (!client_name) {
2775 ERRARG("client_name");
2776 return -1;
2777 } else if (!endpt_name) {
2778 ERRARG("endpt_name");
2779 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002780 } else if (!ti) {
2781 ERRARG("ti");
2782 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002783 }
2784
2785 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002786 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002787 if (!client) {
2788 return -1;
2789 }
2790
2791 for (i = 0; i < client->ch_endpt_count; ++i) {
2792 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002793 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002794 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002795 }
2796 }
2797
2798 ++client->ch_endpt_count;
2799 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2800 if (!client->ch_endpts) {
2801 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002802 goto cleanup;
2803 }
2804 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2805
2806 memset(endpt, 0, sizeof *client->ch_endpts);
Michal Vasko93224072021-11-09 12:14:28 +01002807 endpt->name = strdup(endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002808 endpt->ti = ti;
2809 endpt->sock_pending = -1;
2810 endpt->ka.idle_time = 1;
2811 endpt->ka.max_probes = 10;
2812 endpt->ka.probe_interval = 5;
2813
2814 switch (ti) {
2815#ifdef NC_ENABLED_SSH
2816 case NC_TI_LIBSSH:
2817 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2818 if (!endpt->opts.ssh) {
2819 ERRMEM;
2820 goto cleanup;
2821 }
2822 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2823 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002824 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002825 break;
2826#endif
2827#ifdef NC_ENABLED_TLS
2828 case NC_TI_OPENSSL:
2829 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2830 if (!endpt->opts.tls) {
2831 ERRMEM;
2832 goto cleanup;
2833 }
2834 break;
2835#endif
2836 default:
2837 ERRINT;
2838 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002839 }
2840
Michal Vaskoadf30f02019-06-24 09:34:47 +02002841 /* success */
2842 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002843
Michal Vaskoadf30f02019-06-24 09:34:47 +02002844cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002845 /* UNLOCK */
2846 nc_server_ch_client_unlock(client);
2847
Michal Vaskoadf30f02019-06-24 09:34:47 +02002848 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002849}
2850
2851API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002852nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002853{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002854 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002855 struct nc_ch_client *client;
2856
2857 if (!client_name) {
2858 ERRARG("client_name");
2859 return -1;
2860 }
2861
2862 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002863 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002864 if (!client) {
2865 return -1;
2866 }
2867
Michal Vaskoadf30f02019-06-24 09:34:47 +02002868 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002869
2870 /* UNLOCK */
2871 nc_server_ch_client_unlock(client);
2872
2873 return ret;
2874}
2875
2876API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002877nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2878{
2879 uint16_t i;
2880 struct nc_ch_client *client = NULL;
2881 int found = 0;
2882
2883 if (!client_name || !endpt_name) {
2884 return found;
2885 }
2886
2887 /* READ LOCK */
2888 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2889
2890 for (i = 0; i < server_opts.ch_client_count; ++i) {
2891 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2892 client = &server_opts.ch_clients[i];
2893 break;
2894 }
2895 }
2896
2897 if (!client) {
2898 goto cleanup;
2899 }
2900
2901 for (i = 0; i < client->ch_endpt_count; ++i) {
2902 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2903 found = 1;
2904 break;
2905 }
2906 }
2907
2908cleanup:
2909 /* UNLOCK */
2910 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2911 return found;
2912}
2913
2914API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002915nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2916{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002917 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002918 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002919
2920 if (!client_name) {
2921 ERRARG("client_name");
2922 return -1;
2923 } else if (!endpt_name) {
2924 ERRARG("endpt_name");
2925 return -1;
2926 } else if (!address) {
2927 ERRARG("address");
2928 return -1;
2929 }
2930
2931 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002932 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2933 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002934 return -1;
2935 }
2936
Michal Vasko93224072021-11-09 12:14:28 +01002937 free(endpt->address);
2938 endpt->address = strdup(address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002939
2940 /* UNLOCK */
2941 nc_server_ch_client_unlock(client);
2942
Michal Vaskoadf30f02019-06-24 09:34:47 +02002943 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002944}
2945
2946API int
2947nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2948{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002949 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002950 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002951
2952 if (!client_name) {
2953 ERRARG("client_name");
2954 return -1;
2955 } else if (!endpt_name) {
2956 ERRARG("endpt_name");
2957 return -1;
2958 } else if (!port) {
2959 ERRARG("port");
2960 return -1;
2961 }
2962
2963 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002964 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2965 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002966 return -1;
2967 }
2968
Michal Vaskoadf30f02019-06-24 09:34:47 +02002969 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002970
2971 /* UNLOCK */
2972 nc_server_ch_client_unlock(client);
2973
ravsz5c5a4422020-03-31 15:53:21 +02002974 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002975}
2976
2977API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002978nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
2979{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002980 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002981 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002982
2983 if (!client_name) {
2984 ERRARG("client_name");
2985 return -1;
2986 } else if (!endpt_name) {
2987 ERRARG("endpt_name");
2988 return -1;
2989 }
2990
2991 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002992 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2993 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002994 return -1;
2995 }
2996
Michal Vaskoadf30f02019-06-24 09:34:47 +02002997 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002998
2999 /* UNLOCK */
3000 nc_server_ch_client_unlock(client);
3001
Michal Vasko9af829a2019-09-12 13:50:00 +02003002 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003003}
3004
3005API int
3006nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
3007 int probe_interval)
3008{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003009 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003010 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003011
3012 if (!client_name) {
3013 ERRARG("client_name");
3014 return -1;
3015 } else if (!endpt_name) {
3016 ERRARG("endpt_name");
3017 return -1;
3018 }
3019
3020 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003021 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3022 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003023 return -1;
3024 }
3025
Michal Vaskoadf30f02019-06-24 09:34:47 +02003026 if (idle_time > -1) {
3027 endpt->ka.idle_time = idle_time;
3028 }
3029 if (max_probes > -1) {
3030 endpt->ka.max_probes = max_probes;
3031 }
3032 if (probe_interval > -1) {
3033 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003034 }
3035
3036 /* UNLOCK */
3037 nc_server_ch_client_unlock(client);
3038
Michal Vasko9af829a2019-09-12 13:50:00 +02003039 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003040}
3041
3042API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003043nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
3044{
3045 struct nc_ch_client *client;
3046
3047 if (!client_name) {
3048 ERRARG("client_name");
3049 return -1;
3050 } else if (!conn_type) {
3051 ERRARG("conn_type");
3052 return -1;
3053 }
3054
3055 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003056 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003057 if (!client) {
3058 return -1;
3059 }
3060
3061 if (client->conn_type != conn_type) {
3062 client->conn_type = conn_type;
3063
3064 /* set default options */
3065 switch (conn_type) {
3066 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003067 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003068 break;
3069 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003070 client->conn.period.period = 60;
3071 client->conn.period.anchor_time = 0;
3072 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003073 break;
3074 default:
3075 ERRINT;
3076 break;
3077 }
3078 }
3079
3080 /* UNLOCK */
3081 nc_server_ch_client_unlock(client);
3082
3083 return 0;
3084}
3085
3086API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003087nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
3088{
3089 struct nc_ch_client *client;
3090
3091 if (!client_name) {
3092 ERRARG("client_name");
3093 return -1;
3094 } else if (!period) {
3095 ERRARG("period");
3096 return -1;
3097 }
3098
3099 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003100 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003101 if (!client) {
3102 return -1;
3103 }
3104
3105 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003106 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003107 /* UNLOCK */
3108 nc_server_ch_client_unlock(client);
3109 return -1;
3110 }
3111
3112 client->conn.period.period = period;
3113
3114 /* UNLOCK */
3115 nc_server_ch_client_unlock(client);
3116
3117 return 0;
3118}
3119
3120API int
3121nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003122{
3123 struct nc_ch_client *client;
3124
3125 if (!client_name) {
3126 ERRARG("client_name");
3127 return -1;
3128 }
3129
3130 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003131 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003132 if (!client) {
3133 return -1;
3134 }
3135
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003136 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003137 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003138 /* UNLOCK */
3139 nc_server_ch_client_unlock(client);
3140 return -1;
3141 }
3142
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003143 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003144
3145 /* UNLOCK */
3146 nc_server_ch_client_unlock(client);
3147
3148 return 0;
3149}
3150
3151API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003152nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003153{
3154 struct nc_ch_client *client;
3155
3156 if (!client_name) {
3157 ERRARG("client_name");
3158 return -1;
3159 }
3160
3161 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003162 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003163 if (!client) {
3164 return -1;
3165 }
3166
3167 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003168 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003169 /* UNLOCK */
3170 nc_server_ch_client_unlock(client);
3171 return -1;
3172 }
3173
3174 client->conn.period.idle_timeout = idle_timeout;
3175
3176 /* UNLOCK */
3177 nc_server_ch_client_unlock(client);
3178
3179 return 0;
3180}
3181
3182API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003183nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
3184{
3185 struct nc_ch_client *client;
3186
3187 if (!client_name) {
3188 ERRARG("client_name");
3189 return -1;
3190 }
3191
3192 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003193 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003194 if (!client) {
3195 return -1;
3196 }
3197
3198 client->start_with = start_with;
3199
3200 /* UNLOCK */
3201 nc_server_ch_client_unlock(client);
3202
3203 return 0;
3204}
3205
3206API int
3207nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
3208{
3209 struct nc_ch_client *client;
3210
3211 if (!client_name) {
3212 ERRARG("client_name");
3213 return -1;
3214 } else if (!max_attempts) {
3215 ERRARG("max_attempts");
3216 return -1;
3217 }
3218
3219 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003220 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003221 if (!client) {
3222 return -1;
3223 }
3224
3225 client->max_attempts = max_attempts;
3226
3227 /* UNLOCK */
3228 nc_server_ch_client_unlock(client);
3229
3230 return 0;
3231}
3232
3233/* client lock is expected to be held */
3234static NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +01003235nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb, void *ctx_cb_data,
3236 struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01003237{
Michal Vasko71090fc2016-05-24 16:37:28 +02003238 NC_MSG_TYPE msgtype;
Michal Vasko93224072021-11-09 12:14:28 +01003239 const struct ly_ctx *ctx;
Michal Vaskob05053d2016-01-22 16:12:06 +01003240 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02003241 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01003242 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01003243
Michal Vasko4c612cd2021-02-05 08:53:42 +01003244 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 +01003245 if (sock < 0) {
Michal Vasko0b30e452021-03-03 10:30:15 +01003246 if (endpt->sock_pending > -1) {
3247 ++endpt->sock_retries;
3248 if (endpt->sock_retries == NC_SOCKET_CH_RETRIES) {
Michal Vasko05532772021-06-03 12:12:38 +02003249 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 +01003250 close(endpt->sock_pending);
3251 endpt->sock_pending = -1;
3252 endpt->sock_retries = 0;
3253 }
Michal Vasko4c612cd2021-02-05 08:53:42 +01003254 }
Michal Vasko71090fc2016-05-24 16:37:28 +02003255 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003256 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00003257 /* no need to store the socket as pending any longer */
3258 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01003259
Michal Vasko93224072021-11-09 12:14:28 +01003260 /* acquire context */
3261 ctx = acquire_ctx_cb(ctx_cb_data);
3262 if (!ctx) {
3263 ERR(NULL, "Failed to acquire context for a new Call Home session.");
3264 close(sock);
3265 free(ip_host);
3266 return NC_MSG_ERROR;
3267 }
3268
3269 /* create session */
Michal Vasko131120a2018-05-29 15:44:02 +02003270 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01003271 if (!(*session)) {
3272 ERRMEM;
3273 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01003274 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02003275 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003276 }
3277 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01003278 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003279 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +01003280 (*session)->host = ip_host;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003281 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01003282
Michal Vaskob05053d2016-01-22 16:12:06 +01003283 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01003284#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02003285 if (endpt->ti == NC_TI_LIBSSH) {
3286 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01003287 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003288 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003289
Michal Vasko71090fc2016-05-24 16:37:28 +02003290 if (ret < 0) {
3291 msgtype = NC_MSG_ERROR;
3292 goto fail;
3293 } else if (!ret) {
3294 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003295 goto fail;
3296 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003297 } else
3298#endif
Radek Krejci53691be2016-02-22 13:58:37 +01003299#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02003300 if (endpt->ti == NC_TI_OPENSSL) {
3301 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01003302 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003303 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003304
Michal Vasko71090fc2016-05-24 16:37:28 +02003305 if (ret < 0) {
3306 msgtype = NC_MSG_ERROR;
3307 goto fail;
3308 } else if (!ret) {
3309 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003310 goto fail;
3311 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003312 } else
3313#endif
3314 {
Michal Vaskob05053d2016-01-22 16:12:06 +01003315 ERRINT;
3316 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003317 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003318 goto fail;
3319 }
3320
3321 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02003322 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003323
3324 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003325 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003326 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003327 goto fail;
3328 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003329
3330 nc_gettimespec_mono(&ts_cur);
3331 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
3332 nc_gettimespec_real(&ts_cur);
3333 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003334 (*session)->status = NC_STATUS_RUNNING;
3335
Michal Vasko71090fc2016-05-24 16:37:28 +02003336 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003337
3338fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003339 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003340 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02003341 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003342}
3343
Michal Vasko2e6defd2016-10-07 15:48:15 +02003344struct nc_ch_client_thread_arg {
3345 char *client_name;
Michal Vasko93224072021-11-09 12:14:28 +01003346 nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb;
3347 nc_server_ch_session_release_ctx_cb release_ctx_cb;
3348 void *ctx_cb_data;
3349 nc_server_ch_new_session_cb new_session_cb;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003350};
3351
3352static struct nc_ch_client *
3353nc_server_ch_client_with_endpt_lock(const char *name)
3354{
3355 struct nc_ch_client *client;
3356
3357 while (1) {
3358 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003359 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003360 if (!client) {
3361 return NULL;
3362 }
3363 if (client->ch_endpt_count) {
3364 return client;
3365 }
3366 /* no endpoints defined yet */
3367
3368 /* UNLOCK */
3369 nc_server_ch_client_unlock(client);
3370
3371 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3372 }
3373
3374 return NULL;
3375}
3376
3377static int
3378nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3379{
Michal Vasko3f05a092018-03-13 10:39:49 +01003380 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003381 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003382 struct timespec ts;
3383 struct nc_ch_client *client;
3384
Michal Vasko2e6defd2016-10-07 15:48:15 +02003385 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003386 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003387
Michal Vasko0db3db52021-03-03 10:45:42 +01003388 session->flags |= NC_SESSION_CALLHOME;
3389
Michal Vasko2e6defd2016-10-07 15:48:15 +02003390 /* give the session to the user */
Michal Vasko93224072021-11-09 12:14:28 +01003391 if (data->new_session_cb(data->client_name, session)) {
Michal Vaskof1c26c22021-04-12 16:34:33 +02003392 /* something is wrong, free the session */
3393 session->flags &= ~NC_SESSION_CALLHOME;
3394
3395 /* CH UNLOCK */
3396 pthread_mutex_unlock(&session->opts.server.ch_lock);
3397
3398 nc_session_free(session, NULL);
Michal Vasko93224072021-11-09 12:14:28 +01003399 goto release_ctx;
Michal Vaskof1c26c22021-04-12 16:34:33 +02003400 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003401
3402 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003403 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003404 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003405
Michal Vasko0db3db52021-03-03 10:45:42 +01003406 /* CH COND WAIT */
Michal Vaskoacf98472021-02-04 15:33:57 +01003407 r = pthread_cond_timedwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, &ts);
Michal Vasko3f05a092018-03-13 10:39:49 +01003408 if (!r) {
3409 /* we were woken up, something probably happened */
3410 if (session->status != NC_STATUS_RUNNING) {
3411 break;
3412 }
3413 } else if (r != ETIMEDOUT) {
Michal Vasko05532772021-06-03 12:12:38 +02003414 ERR(session, "Pthread condition timedwait failed (%s).", strerror(r));
Michal Vasko3f05a092018-03-13 10:39:49 +01003415 ret = -1;
3416 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003417 }
3418
Michal Vasko2e6defd2016-10-07 15:48:15 +02003419 /* check whether the client was not removed */
3420 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003421 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003422 if (!client) {
3423 /* client was removed, finish thread */
Michal Vasko05532772021-06-03 12:12:38 +02003424 VRB(session, "Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003425 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003426 ret = 1;
3427 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003428 }
3429
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003430 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003431 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003432 } else {
3433 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003434 }
3435
Michal Vasko9fb42272017-10-05 13:50:05 +02003436 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003437 if (!session->opts.server.ntf_status && idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
Michal Vasko05532772021-06-03 12:12:38 +02003438 VRB(session, "Call Home client \"%s\": session idle timeout elapsed.", client->name);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003439 session->status = NC_STATUS_INVALID;
3440 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3441 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003442
3443 /* UNLOCK */
3444 nc_server_ch_client_unlock(client);
3445
3446 } while (session->status == NC_STATUS_RUNNING);
3447
Michal Vasko0db3db52021-03-03 10:45:42 +01003448 /* signal to nc_session_free() that CH registered this session not being valid anymore */
3449 session->flags &= ~NC_SESSION_CALLHOME;
3450
Michal Vasko27377422018-03-15 08:59:35 +01003451 /* CH UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003452 pthread_mutex_unlock(&session->opts.server.ch_lock);
Michal Vasko27377422018-03-15 08:59:35 +01003453
Michal Vasko93224072021-11-09 12:14:28 +01003454release_ctx:
3455 /* session terminated, release its context */
3456 data->release_ctx_cb(data->ctx_cb_data);
3457
Michal Vasko3f05a092018-03-13 10:39:49 +01003458 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003459}
3460
3461static void *
3462nc_ch_client_thread(void *arg)
3463{
3464 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3465 NC_MSG_TYPE msgtype;
3466 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003467 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003468 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003469 struct nc_ch_endpt *cur_endpt;
3470 struct nc_session *session;
3471 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003472 uint32_t client_id;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003473 time_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003474
3475 /* LOCK */
3476 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3477 if (!client) {
3478 goto cleanup;
3479 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003480 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003481
3482 cur_endpt = &client->ch_endpts[0];
3483 cur_endpt_name = strdup(cur_endpt->name);
3484
Michal Vasko05532772021-06-03 12:12:38 +02003485 VRB(NULL, "Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003486 while (1) {
Michal Vasko93224072021-11-09 12:14:28 +01003487 msgtype = nc_connect_ch_endpt(cur_endpt, data->acquire_ctx_cb, data->ctx_cb_data, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003488
3489 if (msgtype == NC_MSG_HELLO) {
3490 /* UNLOCK */
3491 nc_server_ch_client_unlock(client);
3492
Michal Vasko05532772021-06-03 12:12:38 +02003493 VRB(NULL, "Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003494 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3495 goto cleanup;
3496 }
Michal Vasko05532772021-06-03 12:12:38 +02003497 VRB(NULL, "Call Home client \"%s\" session terminated.", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003498
3499 /* LOCK */
3500 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3501 if (!client) {
3502 goto cleanup;
3503 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003504 if (client->id != client_id) {
3505 nc_server_ch_client_unlock(client);
3506 goto cleanup;
3507 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003508
3509 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003510 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003511 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko18e1fa02021-11-29 09:02:05 +01003512 if (client->conn.period.anchor_time) {
3513 /* anchored */
3514 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
3515 } else {
3516 /* fixed timeout */
3517 reconnect_in = client->conn.period.period * 60;
3518 }
3519
Michal Vasko2e6defd2016-10-07 15:48:15 +02003520 /* UNLOCK */
3521 nc_server_ch_client_unlock(client);
3522
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003523 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
Michal Vasko05532772021-06-03 12:12:38 +02003524 VRB(NULL, "Call Home client \"%s\" reconnecting in %d seconds.", data->client_name, reconnect_in);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003525 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003526
3527 /* LOCK */
3528 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3529 if (!client) {
3530 goto cleanup;
3531 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003532 if (client->id != client_id) {
3533 nc_server_ch_client_unlock(client);
3534 goto cleanup;
3535 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003536 }
3537
3538 /* set next endpoint to try */
3539 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003540 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003541 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003542 /* we keep the current one but due to unlock/lock we have to find it again */
3543 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3544 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3545 break;
3546 }
3547 }
3548 if (next_endpt_index >= client->ch_endpt_count) {
3549 /* endpoint was removed, start with the first one */
3550 next_endpt_index = 0;
3551 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003552 } else {
3553 /* just get a random index */
3554 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003555 }
3556
Michal Vasko2e6defd2016-10-07 15:48:15 +02003557 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003558 /* UNLOCK */
3559 nc_server_ch_client_unlock(client);
3560
Michal Vasko2e6defd2016-10-07 15:48:15 +02003561 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003562 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3563
Michal Vasko6bb116b2016-10-26 13:53:46 +02003564 /* LOCK */
3565 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3566 if (!client) {
3567 goto cleanup;
3568 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003569 if (client->id != client_id) {
3570 nc_server_ch_client_unlock(client);
3571 goto cleanup;
3572 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003573
Michal Vasko2e6defd2016-10-07 15:48:15 +02003574 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003575
3576 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003577 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3578 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003579 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003580 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003581 }
3582
Peter Feiged05f2252018-09-03 08:09:47 +00003583 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003584 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003585 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003586 cur_attempts = 0;
3587 } else if (cur_attempts == client->max_attempts) {
3588 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003589 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003590 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003591 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003592 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003593 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003594 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003595 }
3596
3597 cur_attempts = 0;
3598 } /* else we keep the current one */
3599 }
Peter Feiged05f2252018-09-03 08:09:47 +00003600
3601 cur_endpt = &client->ch_endpts[next_endpt_index];
3602 free(cur_endpt_name);
3603 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003604 }
3605
3606cleanup:
Michal Vasko05532772021-06-03 12:12:38 +02003607 VRB(NULL, "Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003608 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003609 free(data->client_name);
3610 free(data);
3611 return NULL;
3612}
3613
3614API int
Michal Vasko93224072021-11-09 12:14:28 +01003615nc_connect_ch_client_dispatch(const char *client_name, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
3616 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 +01003617{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003618 int ret;
3619 pthread_t tid;
3620 struct nc_ch_client_thread_arg *arg;
3621
3622 if (!client_name) {
3623 ERRARG("client_name");
3624 return -1;
Michal Vasko93224072021-11-09 12:14:28 +01003625 } else if (!acquire_ctx_cb) {
3626 ERRARG("acquire_ctx_cb");
3627 return -1;
3628 } else if (!release_ctx_cb) {
3629 ERRARG("release_ctx_cb");
3630 return -1;
3631 } else if (!new_session_cb) {
3632 ERRARG("new_session_cb");
Michal Vasko2e6defd2016-10-07 15:48:15 +02003633 return -1;
3634 }
3635
3636 arg = malloc(sizeof *arg);
3637 if (!arg) {
3638 ERRMEM;
3639 return -1;
3640 }
3641 arg->client_name = strdup(client_name);
3642 if (!arg->client_name) {
3643 ERRMEM;
3644 free(arg);
3645 return -1;
3646 }
Michal Vasko93224072021-11-09 12:14:28 +01003647 arg->acquire_ctx_cb = acquire_ctx_cb;
3648 arg->release_ctx_cb = release_ctx_cb;
3649 arg->ctx_cb_data = ctx_cb_data;
3650 arg->new_session_cb = new_session_cb;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003651
3652 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3653 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02003654 ERR(NULL, "Creating a new thread failed (%s).", strerror(ret));
Michal Vasko2e6defd2016-10-07 15:48:15 +02003655 free(arg->client_name);
3656 free(arg);
3657 return -1;
3658 }
3659 /* the thread now manages arg */
3660
3661 pthread_detach(tid);
3662
3663 return 0;
3664}
3665
Radek Krejci53691be2016-02-22 13:58:37 +01003666#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003667
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003668API time_t
3669nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003670{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003671 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003672 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003673 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003674 }
3675
Michal Vasko2e6defd2016-10-07 15:48:15 +02003676 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003677}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003678
3679API void
Michal Vasko71dbd772021-03-23 14:08:37 +01003680nc_session_inc_notif_status(struct nc_session *session)
Michal Vasko3486a7c2017-03-03 13:28:07 +01003681{
3682 if (!session || (session->side != NC_SERVER)) {
3683 ERRARG("session");
3684 return;
3685 }
3686
Michal Vasko71dbd772021-03-23 14:08:37 +01003687 ++session->opts.server.ntf_status;
3688}
3689
3690API void
3691nc_session_dec_notif_status(struct nc_session *session)
3692{
3693 if (!session || (session->side != NC_SERVER)) {
3694 ERRARG("session");
3695 return;
3696 }
3697
3698 if (session->opts.server.ntf_status) {
3699 --session->opts.server.ntf_status;
3700 }
Michal Vasko3486a7c2017-03-03 13:28:07 +01003701}
3702
3703API int
3704nc_session_get_notif_status(const struct nc_session *session)
3705{
3706 if (!session || (session->side != NC_SERVER)) {
3707 ERRARG("session");
3708 return 0;
3709 }
3710
3711 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003712}
Michal Vasko8f430592019-02-26 08:32:54 +01003713
3714API int
3715nc_session_is_callhome(const struct nc_session *session)
3716{
3717 if (!session || (session->side != NC_SERVER)) {
3718 ERRARG("session");
3719 return 0;
3720 }
3721
3722 if (session->flags & NC_SESSION_CALLHOME) {
3723 return 1;
3724 }
3725
3726 return 0;
3727}