blob: aeebb25f9259b37cd120aedbdb0a651de6fb94ba [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_server.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libnetconf2 server session manipulation functions
Michal Vasko086311b2016-01-08 09:53:11 +01005 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01006 * @copyright
7 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01008 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01009 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010012 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010013 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010014 */
apropp-molex4e903c32020-04-20 03:06:58 -040015#define _QNX_SOURCE /* getpeereid */
Olivier Matzac7fa2f2018-10-11 10:02:04 +020016#define _GNU_SOURCE /* signals, threads, SO_PEERCRED */
Michal Vasko086311b2016-01-08 09:53:11 +010017
Michal Vaskob83a3fa2021-05-26 09:53:42 +020018#include <arpa/inet.h>
Michal Vasko086311b2016-01-08 09:53:11 +010019#include <errno.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020020#include <fcntl.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020021#include <netinet/in.h>
22#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020023#include <poll.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010024#include <pthread.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020025#include <pwd.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020026#include <signal.h>
27#include <stdint.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/socket.h>
31#include <sys/types.h>
32#include <sys/un.h>
33#include <time.h>
34#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010035
Michal Vasko7a20d2e2021-05-19 16:40:23 +020036#include "compat.h"
Michal Vasko1a38c862016-01-15 15:50:07 +010037#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010038#include "session_server.h"
Michal Vasko0bdf70b2019-06-24 19:20:20 +020039#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010040
Michal Vaskob48aa812016-01-18 14:13:09 +010041struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010042#ifdef NC_ENABLED_SSH
43 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
44#endif
45 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020046 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
47 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010048};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010049
fanchanghu966f2de2016-07-21 02:28:57 -040050static nc_rpc_clb global_rpc_clb = NULL;
51
Michal Vasko3031aae2016-01-27 16:07:18 +010052struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010053nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010054{
55 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010056 struct nc_endpt *endpt = NULL;
57
Michal Vaskoddce1212019-05-24 09:58:49 +020058 if (!name) {
59 ERRARG("endpt_name");
60 return NULL;
61 }
62
Michal Vaskoade892d2017-02-22 13:40:35 +010063 /* WRITE LOCK */
64 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010065
66 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020067 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010068 endpt = &server_opts.endpts[i];
69 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010070 }
71 }
72
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010073 if (!endpt) {
Michal Vasko05532772021-06-03 12:12:38 +020074 ERR(NULL, "Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010075 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020076 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010077 return NULL;
78 }
79
Michal Vaskoe2713da2016-08-22 16:06:40 +020080 if (idx) {
81 *idx = i;
82 }
83
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010084 return endpt;
85}
86
Michal Vaskoadf30f02019-06-24 09:34:47 +020087struct nc_ch_endpt *
88nc_server_ch_client_lock(const char *name, const char *endpt_name, NC_TRANSPORT_IMPL ti, struct nc_ch_client **client_p)
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010089{
Michal Vaskoadf30f02019-06-24 09:34:47 +020090 uint16_t i, j;
Michal Vasko2e6defd2016-10-07 15:48:15 +020091 struct nc_ch_client *client = NULL;
Michal Vaskoadf30f02019-06-24 09:34:47 +020092 struct nc_ch_endpt *endpt = NULL;
93
94 *client_p = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +020095
Michal Vaskoddce1212019-05-24 09:58:49 +020096 if (!name) {
97 ERRARG("client_name");
98 return NULL;
99 }
100
Michal Vasko2e6defd2016-10-07 15:48:15 +0200101 /* READ LOCK */
102 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
103
104 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200105 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200106 client = &server_opts.ch_clients[i];
Michal Vaskoadf30f02019-06-24 09:34:47 +0200107 if (!endpt_name && !ti) {
108 /* return only client */
109 break;
110 }
111 for (j = 0; j < client->ch_endpt_count; ++j) {
Michal Vasko530d95c2021-05-28 13:32:02 +0200112 if ((!endpt_name || !strcmp(client->ch_endpts[j].name, endpt_name)) &&
113 (!ti || (ti == client->ch_endpts[j].ti))) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200114 endpt = &client->ch_endpts[j];
115 break;
116 }
117 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200118 break;
119 }
120 }
121
122 if (!client) {
Michal Vasko05532772021-06-03 12:12:38 +0200123 ERR(NULL, "Call Home client \"%s\" was not found.", name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200124
Michal Vasko2e6defd2016-10-07 15:48:15 +0200125 /* READ UNLOCK */
126 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200127 } else if (endpt_name && ti && !endpt) {
Michal Vasko05532772021-06-03 12:12:38 +0200128 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" was not found.", name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200129
130 /* READ UNLOCK */
131 pthread_rwlock_unlock(&server_opts.ch_client_lock);
132 } else {
133 /* CH CLIENT LOCK */
134 pthread_mutex_lock(&client->lock);
135
136 *client_p = client;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200137 }
138
Michal Vaskoadf30f02019-06-24 09:34:47 +0200139 return endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200140}
141
142void
143nc_server_ch_client_unlock(struct nc_ch_client *client)
144{
145 /* CH CLIENT UNLOCK */
146 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100147
148 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200149 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100150}
Michal Vasko086311b2016-01-08 09:53:11 +0100151
Michal Vasko1a38c862016-01-15 15:50:07 +0100152API void
153nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
154{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200155 if (!session) {
156 ERRARG("session");
157 return;
158 } else if (!reason) {
159 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100160 return;
161 }
162
Michal Vasko142cfea2017-08-07 10:12:11 +0200163 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
164 session->killed_by = 0;
165 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100166 session->term_reason = reason;
167}
168
Michal Vasko142cfea2017-08-07 10:12:11 +0200169API void
170nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
171{
172 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
173 ERRARG("session");
174 return;
175 } else if (!sid) {
176 ERRARG("sid");
177 return;
178 }
179
180 session->killed_by = sid;
181}
182
183API void
184nc_session_set_status(struct nc_session *session, NC_STATUS status)
185{
186 if (!session) {
187 ERRARG("session");
188 return;
189 } else if (!status) {
190 ERRARG("status");
191 return;
192 }
193
194 session->status = status;
195}
196
Michal Vasko086311b2016-01-08 09:53:11 +0100197int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200198nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100199{
Michal Vasko06c860d2018-07-09 16:08:52 +0200200 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100201 int is_ipv4, sock;
202 struct sockaddr_storage saddr;
203
204 struct sockaddr_in *saddr4;
205 struct sockaddr_in6 *saddr6;
206
Michal Vasko086311b2016-01-08 09:53:11 +0100207 if (!strchr(address, ':')) {
208 is_ipv4 = 1;
209 } else {
210 is_ipv4 = 0;
211 }
212
213 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
214 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200215 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100216 goto fail;
217 }
218
Michal Vaskobe52dc22018-10-17 09:28:17 +0200219 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200220 opt = 1;
221 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200222 ERR(NULL, "Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko06c860d2018-07-09 16:08:52 +0200223 goto fail;
224 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100225 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200226 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vasko83ad17e2019-01-30 10:11:37 +0100227 goto fail;
228 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200229
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200230 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100231 goto fail;
232 }
233
Michal Vaskof22d5ff2020-04-15 11:10:27 +0200234 memset(&saddr, 0, sizeof(struct sockaddr_storage));
Michal Vasko086311b2016-01-08 09:53:11 +0100235 if (is_ipv4) {
236 saddr4 = (struct sockaddr_in *)&saddr;
237
238 saddr4->sin_family = AF_INET;
239 saddr4->sin_port = htons(port);
240
241 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200242 ERR(NULL, "Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100243 goto fail;
244 }
245
246 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200247 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100248 goto fail;
249 }
250
251 } else {
252 saddr6 = (struct sockaddr_in6 *)&saddr;
253
254 saddr6->sin6_family = AF_INET6;
255 saddr6->sin6_port = htons(port);
256
257 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200258 ERR(NULL, "Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100259 goto fail;
260 }
261
262 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200263 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100264 goto fail;
265 }
266 }
267
Michal Vaskofb89d772016-01-08 12:25:35 +0100268 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200269 ERR(NULL, "Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100270 goto fail;
271 }
272
273 return sock;
274
275fail:
276 if (sock > -1) {
277 close(sock);
278 }
279
280 return -1;
281}
282
283int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200284nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
285{
286 struct sockaddr_un sun;
287 int sock = -1;
288
Michal Vasko93e96f12021-09-30 10:02:09 +0200289 if (strlen(address) > sizeof(sun.sun_path) - 1) {
290 ERR(NULL, "Socket path \"%s\" is longer than maximum length %d.", address, (int)(sizeof(sun.sun_path) - 1));
291 goto fail;
292 }
293
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200294 sock = socket(AF_UNIX, SOCK_STREAM, 0);
295 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200296 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200297 goto fail;
298 }
299
300 memset(&sun, 0, sizeof(sun));
301 sun.sun_family = AF_UNIX;
Michal Vasko93e96f12021-09-30 10:02:09 +0200302 snprintf(sun.sun_path, sizeof(sun.sun_path) - 1, "%s", address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200303
304 unlink(sun.sun_path);
305 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200306 ERR(NULL, "Could not bind \"%s\" (%s).", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200307 goto fail;
308 }
309
310 if (opts->mode != (mode_t)-1) {
311 if (chmod(sun.sun_path, opts->mode) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200312 ERR(NULL, "Failed to set unix socket permissions (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200313 goto fail;
314 }
315 }
316
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200317 if ((opts->uid != (uid_t)-1) || (opts->gid != (gid_t)-1)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200318 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200319 ERR(NULL, "Failed to set unix socket uid/gid (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200320 goto fail;
321 }
322 }
323
324 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200325 ERR(NULL, "Unable to start listening on \"%s\" (%s).", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200326 goto fail;
327 }
328
329 return sock;
330
331fail:
332 if (sock > -1) {
333 close(sock);
334 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200335 return -1;
336}
337
aPiecek90ff0242021-02-14 14:58:01 +0100338/**
339 * @brief Evaluate socket name for AF_UNIX socket.
340 * @param[in] acc_sock_fd is file descriptor for the accepted socket (a nonnegative).
341 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
342 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
343 * @return 0 if the stream socket is unnamed. Parameter host is set to NULL.
344 * @return -1 in case of error. Parameter host is set to NULL.
345 */
346static int
347sock_host_unix(int acc_sock_fd, char **host)
348{
349 char *sun_path;
350 struct sockaddr_storage saddr;
351 socklen_t addr_len;
352
353 *host = NULL;
354 saddr.ss_family = AF_UNIX;
355 addr_len = sizeof(saddr);
356
357 if (getsockname(acc_sock_fd, (struct sockaddr *)&saddr, &addr_len)) {
Michal Vasko05532772021-06-03 12:12:38 +0200358 ERR(NULL, "getsockname failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100359 return -1;
360 }
361
362 sun_path = ((struct sockaddr_un *)&saddr)->sun_path;
363 if (!sun_path) {
364 /* stream socket is unnamed */
365 return 0;
366 }
367
368 if (!(*host = strdup(sun_path))) {
369 ERRMEM;
370 return -1;
371 }
372
373 return 0;
374}
375
376/**
377 * @brief Evaluate socket name and port number for AF_INET socket.
378 * @param[in] addr is pointing to structure filled by accept function which was successful.
379 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
380 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
381 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
382 * @return -1 in case of error. Parameter host is set to NULL and port is unchanged.
383 */
384static int
385sock_host_inet(const struct sockaddr_in *addr, char **host, uint16_t *port)
386{
387 *host = malloc(INET_ADDRSTRLEN);
388 if (!(*host)) {
389 ERRMEM;
390 return -1;
391 }
392
aPiecek3da9b342021-02-18 15:00:03 +0100393 if (!inet_ntop(AF_INET, &addr->sin_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +0200394 ERR(NULL, "inet_ntop failed(%s).");
aPiecek90ff0242021-02-14 14:58:01 +0100395 free(*host);
396 *host = NULL;
397 return -1;
398 }
399
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200400 *port = ntohs(addr->sin_port);
aPiecek90ff0242021-02-14 14:58:01 +0100401
402 return 0;
403}
404
405/**
406 * @brief Evaluate socket name and port number for AF_INET6 socket.
407 * @param[in] addr is pointing to structure filled by accept function which was successful.
408 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
409 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
410 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
411 * @return -1 in case of error. Parameter host is set to the NULL and port is unchanged.
412 */
413static int
414sock_host_inet6(const struct sockaddr_in6 *addr, char **host, uint16_t *port)
415{
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200416 *host = malloc(INET6_ADDRSTRLEN);
aPiecek90ff0242021-02-14 14:58:01 +0100417 if (!(*host)) {
418 ERRMEM;
419 return -1;
420 }
421
aPiecek3da9b342021-02-18 15:00:03 +0100422 if (!inet_ntop(AF_INET6, &addr->sin6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +0200423 ERR(NULL, "inet_ntop failed(%s).");
aPiecek90ff0242021-02-14 14:58:01 +0100424 free(*host);
425 *host = NULL;
426 return -1;
427 }
428
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200429 *port = ntohs(addr->sin6_port);
aPiecek90ff0242021-02-14 14:58:01 +0100430
431 return 0;
432}
433
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200434int
Michal Vasko3031aae2016-01-27 16:07:18 +0100435nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx)
Michal Vasko086311b2016-01-08 09:53:11 +0100436{
Michal Vaskof54cd352017-02-22 13:42:02 +0100437 sigset_t sigmask, origmask;
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200438 uint16_t i, j, pfd_count, client_port;
439 char *client_address;
Michal Vasko086311b2016-01-08 09:53:11 +0100440 struct pollfd *pfd;
441 struct sockaddr_storage saddr;
442 socklen_t saddr_len = sizeof(saddr);
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200443 int ret, client_sock, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100444
445 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100446 if (!pfd) {
447 ERRMEM;
448 return -1;
449 }
450
Michal Vaskoac2f6182017-01-30 14:32:03 +0100451 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200452 if (binds[i].sock < 0) {
453 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200454 continue;
455 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200456 if (binds[i].pollin) {
457 binds[i].pollin = 0;
458 /* leftover pollin */
459 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100460 break;
461 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100462 pfd[pfd_count].fd = binds[i].sock;
463 pfd[pfd_count].events = POLLIN;
464 pfd[pfd_count].revents = 0;
465
466 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100467 }
468
Michal Vasko0a3f3752016-10-13 14:58:38 +0200469 if (sock == -1) {
470 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100471 sigfillset(&sigmask);
472 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100473 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100474 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
475
Michal Vasko0a3f3752016-10-13 14:58:38 +0200476 if (!ret) {
477 /* we timeouted */
478 free(pfd);
479 return 0;
480 } else if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200481 ERR(NULL, "Poll failed (%s).", strerror(errno));
Michal Vasko0a3f3752016-10-13 14:58:38 +0200482 free(pfd);
483 return -1;
484 }
Michal Vasko086311b2016-01-08 09:53:11 +0100485
Michal Vaskoac2f6182017-01-30 14:32:03 +0100486 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
487 /* adjust i so that indices in binds and pfd always match */
488 while (binds[i].sock != pfd[j].fd) {
489 ++i;
490 }
491
492 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200493 --ret;
494
495 if (!ret) {
496 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100497 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200498 break;
499 } else {
500 /* just remember the event for next time */
501 binds[i].pollin = 1;
502 }
503 }
Michal Vasko086311b2016-01-08 09:53:11 +0100504 }
505 }
506 free(pfd);
507
508 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100509 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100510 return -1;
511 }
512
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200513 /* accept connection */
514 client_sock = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
515 if (client_sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200516 ERR(NULL, "Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100517 return -1;
518 }
519
Michal Vasko0190bc32016-03-02 15:47:49 +0100520 /* make the socket non-blocking */
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200521 if (((flags = fcntl(client_sock, F_GETFL)) == -1) || (fcntl(client_sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200522 ERR(NULL, "Fcntl failed (%s).", strerror(errno));
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200523 goto fail;
Michal Vasko0190bc32016-03-02 15:47:49 +0100524 }
525
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200526 /* learn information about the client end */
527 if (saddr.ss_family == AF_UNIX) {
528 if (sock_host_unix(client_sock, &client_address)) {
529 goto fail;
530 }
531 client_port = 0;
532 } else if (saddr.ss_family == AF_INET) {
533 if (sock_host_inet((struct sockaddr_in *)&saddr, &client_address, &client_port)) {
534 goto fail;
535 }
536 } else if (saddr.ss_family == AF_INET6) {
537 if (sock_host_inet6((struct sockaddr_in6 *)&saddr, &client_address, &client_port)) {
538 goto fail;
539 }
540 } else {
541 ERR(NULL, "Source host of an unknown protocol family.");
542 goto fail;
aPiecek90ff0242021-02-14 14:58:01 +0100543 }
Michal Vasko086311b2016-01-08 09:53:11 +0100544
aPiecek90ff0242021-02-14 14:58:01 +0100545 if (saddr.ss_family == AF_UNIX) {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200546 VRB(NULL, "Accepted a connection on %s.", binds[i].address);
aPiecek90ff0242021-02-14 14:58:01 +0100547 } else {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200548 VRB(NULL, "Accepted a connection on %s:%u from %s:%u.", binds[i].address, binds[i].port, client_address, client_port);
Michal Vasko086311b2016-01-08 09:53:11 +0100549 }
550
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200551 if (host) {
552 *host = client_address;
553 } else {
554 free(client_address);
555 }
556 if (port) {
557 *port = client_port;
558 }
559 if (idx) {
560 *idx = i;
561 }
562 return client_sock;
563
564fail:
565 close(client_sock);
566 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100567}
568
Michal Vasko238b6c12021-12-14 15:14:09 +0100569API struct nc_server_reply *
Michal Vasko05532772021-06-03 12:12:38 +0200570nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100571{
Michal Vasko77367452021-02-16 16:32:18 +0100572 const char *identifier = NULL, *revision = NULL, *format = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100573 char *model_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100574 struct ly_out *out;
Michal Vasko9b1a9522021-03-15 16:24:26 +0100575 const struct lys_module *module = NULL, *mod;
Michal Vasko77367452021-02-16 16:32:18 +0100576 const struct lysp_submodule *submodule = NULL;
577 struct lyd_node *child, *err, *data = NULL;
578 LYS_OUTFORMAT outformat = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100579
Michal Vasko77367452021-02-16 16:32:18 +0100580 LY_LIST_FOR(lyd_child(rpc), child) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100581 if (!strcmp(child->schema->name, "identifier")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200582 identifier = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100583 } else if (!strcmp(child->schema->name, "version")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200584 revision = lyd_get_value(child);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200585 if (revision && (revision[0] == '\0')) {
Michal Vasko77367452021-02-16 16:32:18 +0100586 revision = NULL;
Radek Krejci1afa7792017-03-26 11:24:16 -0500587 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100588 } else if (!strcmp(child->schema->name, "format")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200589 format = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100590 }
591 }
Michal Vasko05532772021-06-03 12:12:38 +0200592 VRB(session, "Schema \"%s@%s\" was requested.", identifier, revision ? revision : "<any>");
Michal Vasko05ba9df2016-01-13 14:40:27 +0100593
Michal Vasko77367452021-02-16 16:32:18 +0100594 /* check revision */
595 if (revision && (strlen(revision) != 10) && strcmp(revision, "1.0")) {
Michal Vasko93224072021-11-09 12:14:28 +0100596 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100597 nc_err_set_msg(err, "The requested version is not supported.", "en");
598 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100599 }
600
Michal Vasko77367452021-02-16 16:32:18 +0100601 if (revision) {
602 /* get specific module */
Michal Vasko93224072021-11-09 12:14:28 +0100603 module = ly_ctx_get_module(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100604 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100605 submodule = ly_ctx_get_submodule(session->ctx, identifier, revision);
Michal Vasko77367452021-02-16 16:32:18 +0100606 }
607 } else {
608 /* try to get implemented, then latest module */
Michal Vasko93224072021-11-09 12:14:28 +0100609 module = ly_ctx_get_module_implemented(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100610 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100611 module = ly_ctx_get_module_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100612 }
613 if (!module) {
Michal Vasko93224072021-11-09 12:14:28 +0100614 submodule = ly_ctx_get_submodule_latest(session->ctx, identifier);
Michal Vasko77367452021-02-16 16:32:18 +0100615 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200616 }
Michal Vasko77367452021-02-16 16:32:18 +0100617 if (!module && !submodule) {
Michal Vasko93224072021-11-09 12:14:28 +0100618 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100619 nc_err_set_msg(err, "The requested schema was not found.", "en");
620 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100621 }
622
623 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100624 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko77367452021-02-16 16:32:18 +0100625 outformat = LYS_OUT_YANG;
Radek Krejci90fba642016-12-07 15:59:45 +0100626 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko77367452021-02-16 16:32:18 +0100627 outformat = LYS_OUT_YIN;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100628 } else {
Michal Vasko93224072021-11-09 12:14:28 +0100629 err = nc_err(session->ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100630 nc_err_set_msg(err, "The requested format is not supported.", "en");
631 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100632 }
Michal Vasko77367452021-02-16 16:32:18 +0100633
634 /* print */
635 ly_out_new_memory(&model_data, 0, &out);
636 if (module) {
637 lys_print_module(out, module, outformat, 0, 0);
638 } else {
639 lys_print_submodule(out, submodule, outformat, 0, 0);
640 }
641 ly_out_free(out, NULL, 0);
Michal Vaskod91f6e62016-04-05 11:34:22 +0200642 if (!model_data) {
643 ERRINT;
644 return NULL;
645 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100646
Michal Vasko9b1a9522021-03-15 16:24:26 +0100647 /* create reply */
Michal Vasko93224072021-11-09 12:14:28 +0100648 mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring");
Michal Vasko9b1a9522021-03-15 16:24:26 +0100649 if (!mod || lyd_new_inner(NULL, mod, "get-schema", 0, &data)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100650 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200651 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100652 return NULL;
653 }
Michal Vasko9b1a9522021-03-15 16:24:26 +0100654 if (lyd_new_any(data, NULL, "data", model_data, 1, LYD_ANYDATA_STRING, 1, NULL)) {
655 ERRINT;
Michal Vaskoa50f68e2022-02-24 16:10:54 +0100656 free(model_data);
Michal Vasko9b1a9522021-03-15 16:24:26 +0100657 lyd_free_tree(data);
658 return NULL;
659 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100660
Radek Krejci36dfdb32016-09-01 16:56:35 +0200661 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100662}
663
Michal Vasko238b6c12021-12-14 15:14:09 +0100664API struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100665nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100666{
Michal Vasko428087d2016-01-14 16:04:28 +0100667 session->term_reason = NC_SESSION_TERM_CLOSED;
668 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100669}
670
Michal Vasko93224072021-11-09 12:14:28 +0100671/**
672 * @brief Initialize a context with default RPC callbacks if none are set.
673 *
674 * @param[in] ctx Context to initialize.
675 */
676static void
677nc_server_init_ctx(const struct ly_ctx *ctx)
Michal Vasko086311b2016-01-08 09:53:11 +0100678{
Michal Vasko77367452021-02-16 16:32:18 +0100679 struct lysc_node *rpc;
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100680
Michal Vasko238b6c12021-12-14 15:14:09 +0100681 if (global_rpc_clb) {
682 /* expect it to handle these RPCs as well */
683 return;
684 }
685
Michal Vasko05ba9df2016-01-13 14:40:27 +0100686 /* set default <get-schema> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100687 rpc = NULL;
688 if (ly_ctx_get_module_implemented(ctx, "ietf-netconf-monitoring")) {
689 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
690 }
Michal Vasko88639e92017-08-03 14:38:10 +0200691 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100692 rpc->priv = nc_clb_default_get_schema;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100693 }
694
Michal Vasko93224072021-11-09 12:14:28 +0100695 /* set default <close-session> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100696 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf:close-session", 0);
Michal Vasko88639e92017-08-03 14:38:10 +0200697 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100698 rpc->priv = nc_clb_default_close_session;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100699 }
Michal Vasko93224072021-11-09 12:14:28 +0100700}
Michal Vasko05ba9df2016-01-13 14:40:27 +0100701
Michal Vasko93224072021-11-09 12:14:28 +0100702API int
703nc_server_init(void)
704{
705 pthread_rwlockattr_t attr, *attr_p = NULL;
706 int r;
707
708 nc_init();
Michal Vaskob48aa812016-01-18 14:13:09 +0100709
710 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500711 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100712
Michal Vasko93224072021-11-09 12:14:28 +0100713#ifdef HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
714 if ((r = pthread_rwlockattr_init(&attr))) {
715 ERR(NULL, "%s: failed init attribute (%s).", __func__, strerror(r));
716 goto error;
717 }
718 attr_p = &attr;
719 if ((r = pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))) {
720 ERR(NULL, "%s: failed set attribute (%s).", __func__, strerror(r));
721 goto error;
722 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700723#endif
Michal Vasko93224072021-11-09 12:14:28 +0100724
725 if ((r = pthread_rwlock_init(&server_opts.endpt_lock, attr_p))) {
726 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
727 goto error;
728 }
729 if ((r = pthread_rwlock_init(&server_opts.ch_client_lock, attr_p))) {
730 ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
731 goto error;
732 }
733
734 if (attr_p) {
735 pthread_rwlockattr_destroy(attr_p);
Frank Rimpler9f838b02018-07-25 06:44:03 +0000736 }
Michal Vasko086311b2016-01-08 09:53:11 +0100737 return 0;
Michal Vasko93224072021-11-09 12:14:28 +0100738
739error:
740 if (attr_p) {
741 pthread_rwlockattr_destroy(attr_p);
742 }
743 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100744}
745
Michal Vaskob48aa812016-01-18 14:13:09 +0100746API void
747nc_server_destroy(void)
748{
Michal Vasko1440a742021-03-31 11:11:03 +0200749 uint32_t i;
Radek Krejci658782b2016-12-04 22:04:55 +0100750
751 for (i = 0; i < server_opts.capabilities_count; i++) {
Michal Vasko93224072021-11-09 12:14:28 +0100752 free(server_opts.capabilities[i]);
Radek Krejci658782b2016-12-04 22:04:55 +0100753 }
754 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200755 server_opts.capabilities = NULL;
756 server_opts.capabilities_count = 0;
Michal Vasko1440a742021-03-31 11:11:03 +0200757 if (server_opts.content_id_data && server_opts.content_id_data_free) {
758 server_opts.content_id_data_free(server_opts.content_id_data);
759 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200760
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200761#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100762 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200763 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100764#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100765#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100766 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
767 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
768 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200769 server_opts.passwd_auth_data = NULL;
770 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100771
Michal Vasko17dfda92016-12-01 14:06:16 +0100772 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100773
774 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
775 server_opts.hostkey_data_free(server_opts.hostkey_data);
776 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200777 server_opts.hostkey_data = NULL;
778 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100779#endif
780#ifdef NC_ENABLED_TLS
781 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
782 server_opts.server_cert_data_free(server_opts.server_cert_data);
783 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200784 server_opts.server_cert_data = NULL;
785 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100786 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
787 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
788 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200789 server_opts.trusted_cert_list_data = NULL;
790 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100791#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100792 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100793}
794
Michal Vasko086311b2016-01-08 09:53:11 +0100795API int
796nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
797{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200798 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
799 ERRARG("basic_mode");
800 return -1;
801 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
802 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100803 return -1;
804 }
805
806 server_opts.wd_basic_mode = basic_mode;
807 server_opts.wd_also_supported = also_supported;
808 return 0;
809}
810
Michal Vasko1a38c862016-01-15 15:50:07 +0100811API void
Michal Vasko55f03972016-04-13 08:56:01 +0200812nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
813{
814 if (!basic_mode && !also_supported) {
815 ERRARG("basic_mode and also_supported");
816 return;
817 }
818
819 if (basic_mode) {
820 *basic_mode = server_opts.wd_basic_mode;
821 }
822 if (also_supported) {
823 *also_supported = server_opts.wd_also_supported;
824 }
825}
826
Michal Vasko55f03972016-04-13 08:56:01 +0200827API int
Radek Krejci658782b2016-12-04 22:04:55 +0100828nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200829{
Michal Vasko93224072021-11-09 12:14:28 +0100830 void *mem;
Radek Krejci658782b2016-12-04 22:04:55 +0100831
832 if (!value || !value[0]) {
833 ERRARG("value must not be empty");
834 return EXIT_FAILURE;
835 }
836
Michal Vasko93224072021-11-09 12:14:28 +0100837 mem = realloc(server_opts.capabilities, (server_opts.capabilities_count + 1) * sizeof *server_opts.capabilities);
838 if (!mem) {
Radek Krejci658782b2016-12-04 22:04:55 +0100839 ERRMEM;
840 return EXIT_FAILURE;
841 }
Michal Vasko93224072021-11-09 12:14:28 +0100842 server_opts.capabilities = mem;
843
844 server_opts.capabilities[server_opts.capabilities_count] = strdup(value);
845 server_opts.capabilities_count++;
Radek Krejci658782b2016-12-04 22:04:55 +0100846
847 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200848}
849
Michal Vasko1a38c862016-01-15 15:50:07 +0100850API void
Michal Vasko1440a742021-03-31 11:11:03 +0200851nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
852 void (*free_user_data)(void *user_data))
853{
854 server_opts.content_id_clb = content_id_clb;
855 server_opts.content_id_data = user_data;
856 server_opts.content_id_data_free = free_user_data;
857}
858
859API void
Michal Vasko086311b2016-01-08 09:53:11 +0100860nc_server_set_hello_timeout(uint16_t hello_timeout)
861{
Michal Vasko086311b2016-01-08 09:53:11 +0100862 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100863}
864
Michal Vasko55f03972016-04-13 08:56:01 +0200865API uint16_t
866nc_server_get_hello_timeout(void)
867{
868 return server_opts.hello_timeout;
869}
870
Michal Vasko1a38c862016-01-15 15:50:07 +0100871API void
Michal Vasko086311b2016-01-08 09:53:11 +0100872nc_server_set_idle_timeout(uint16_t idle_timeout)
873{
Michal Vasko086311b2016-01-08 09:53:11 +0100874 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100875}
876
Michal Vasko55f03972016-04-13 08:56:01 +0200877API uint16_t
878nc_server_get_idle_timeout(void)
879{
880 return server_opts.idle_timeout;
881}
882
Michal Vasko71090fc2016-05-24 16:37:28 +0200883API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +0100884nc_accept_inout(int fdin, int fdout, const char *username, const struct ly_ctx *ctx, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100885{
Michal Vasko71090fc2016-05-24 16:37:28 +0200886 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200887 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200888
Michal Vasko93224072021-11-09 12:14:28 +0100889 if (!ctx) {
890 ERRARG("ctx");
Michal Vasko71090fc2016-05-24 16:37:28 +0200891 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200892 } else if (fdin < 0) {
893 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200894 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200895 } else if (fdout < 0) {
896 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200897 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200898 } else if (!username) {
899 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200900 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200901 } else if (!session) {
902 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200903 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100904 }
905
Michal Vasko93224072021-11-09 12:14:28 +0100906 /* init ctx as needed */
907 nc_server_init_ctx(ctx);
908
Michal Vasko086311b2016-01-08 09:53:11 +0100909 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200910 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100911 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100912 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200913 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100914 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100915 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100916
Michal Vasko086311b2016-01-08 09:53:11 +0100917 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100918 (*session)->ti_type = NC_TI_FD;
919 (*session)->ti.fd.in = fdin;
920 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100921
Michal Vasko93224072021-11-09 12:14:28 +0100922 /* assign context */
Michal Vasko1a38c862016-01-15 15:50:07 +0100923 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +0100924 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100925
Michal Vaskob48aa812016-01-18 14:13:09 +0100926 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200927 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100928
Michal Vasko086311b2016-01-08 09:53:11 +0100929 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200930 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200931 if (msgtype != NC_MSG_HELLO) {
932 nc_session_free(*session, NULL);
933 *session = NULL;
934 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100935 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200936
937 nc_gettimespec_mono(&ts_cur);
938 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
939 nc_gettimespec_real(&ts_cur);
940 (*session)->opts.server.session_start = ts_cur.tv_sec;
941
Michal Vasko1a38c862016-01-15 15:50:07 +0100942 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100943
Michal Vasko71090fc2016-05-24 16:37:28 +0200944 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100945}
Michal Vasko9e036d52016-01-08 10:49:26 +0100946
Michal Vaskob30b99c2016-07-26 11:35:43 +0200947static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100948nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
949{
950 uint8_t q_last;
951
952 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
953 ERRINT;
954 return;
955 }
956
957 /* get a unique queue value (by adding 1 to the last added value, if any) */
958 if (ps->queue_len) {
959 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
960 *id = ps->queue[q_last] + 1;
961 } else {
962 *id = 0;
963 }
964
965 /* add the id into the queue */
966 ++ps->queue_len;
967 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
968 ps->queue[q_last] = *id;
969}
970
971static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200972nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
973{
Michal Vasko74c345f2018-02-07 10:37:11 +0100974 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200975
976 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100977 /* get the actual queue idx */
978 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200979
980 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100981 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200982 /* another equal value, simply cannot be */
983 ERRINT;
984 }
Michal Vaskod8340032018-02-12 14:41:00 +0100985 if (found == 2) {
986 /* move the following values */
987 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
988 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100989 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200990 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100991 if (i == 0) {
992 found = 1;
993 } else {
994 /* this is not okay, our id is in the middle of the queue */
995 found = 2;
996 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200997 }
998 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200999 if (!found) {
1000 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +01001001 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001002 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001003
Michal Vasko103fe632018-02-12 16:37:45 +01001004 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +01001005 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +01001006 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +01001007 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
1008 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001009}
1010
Michal Vaskof04a52a2016-04-07 10:52:10 +02001011int
Michal Vasko26043172016-07-26 14:08:59 +02001012nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001013{
1014 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001015 struct timespec ts;
1016
Michal Vasko77a6abe2017-10-05 10:02:20 +02001017 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +01001018 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001019
1020 /* LOCK */
1021 ret = pthread_mutex_timedlock(&ps->lock, &ts);
1022 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001023 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001024 return -1;
1025 }
1026
Michal Vasko74c345f2018-02-07 10:37:11 +01001027 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +01001028 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko05532772021-06-03 12:12:38 +02001029 ERR(NULL, "%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +01001030 pthread_mutex_unlock(&ps->lock);
1031 return -1;
1032 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001033
1034 /* add ourselves into the queue */
1035 nc_ps_queue_add_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001036 DBL(NULL, "PS 0x%p TID %lu queue: added %u, head %u, length %u", ps, (long unsigned int)pthread_self(), *id,
Michal Vasko91290952019-09-27 11:30:55 +02001037 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001038
1039 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001040 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001041 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +01001042 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001043
1044 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
1045 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +05301046 /**
1047 * This may happen when another thread releases the lock and broadcasts the condition
1048 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
1049 * but when actually this thread was ready for condition.
1050 */
preetbhansali629dfc42018-12-17 16:04:40 +05301051 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +05301052 break;
1053 }
Michal Vasko66032bc2019-01-22 15:03:12 +01001054
Michal Vasko05532772021-06-03 12:12:38 +02001055 ERR(NULL, "%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001056 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001057 nc_ps_queue_remove_id(ps, *id);
1058 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001059 return -1;
1060 }
1061 }
1062
Michal Vaskobe86fe32016-04-07 10:43:03 +02001063 /* UNLOCK */
1064 pthread_mutex_unlock(&ps->lock);
1065
1066 return 0;
1067}
1068
Michal Vaskof04a52a2016-04-07 10:52:10 +02001069int
Michal Vasko26043172016-07-26 14:08:59 +02001070nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001071{
1072 int ret;
1073 struct timespec ts;
1074
Michal Vasko77a6abe2017-10-05 10:02:20 +02001075 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +01001076 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001077
1078 /* LOCK */
1079 ret = pthread_mutex_timedlock(&ps->lock, &ts);
1080 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001081 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001082 ret = -1;
1083 }
1084
Michal Vaskob30b99c2016-07-26 11:35:43 +02001085 /* we must be the first, it was our turn after all, right? */
1086 if (ps->queue[ps->queue_begin] != id) {
1087 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +02001088 /* UNLOCK */
1089 if (!ret) {
1090 pthread_mutex_unlock(&ps->lock);
1091 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001092 return -1;
1093 }
1094
Michal Vaskobe86fe32016-04-07 10:43:03 +02001095 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001096 nc_ps_queue_remove_id(ps, id);
Michal Vaskofdba4a32022-01-05 12:13:53 +01001097 DBL(NULL, "PS 0x%p TID %lu queue: removed %u, head %u, length %u", ps, (long unsigned int)pthread_self(), id,
Michal Vasko91290952019-09-27 11:30:55 +02001098 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001099
1100 /* broadcast to all other threads that the queue moved */
1101 pthread_cond_broadcast(&ps->cond);
1102
Michal Vaskobe86fe32016-04-07 10:43:03 +02001103 /* UNLOCK */
1104 if (!ret) {
1105 pthread_mutex_unlock(&ps->lock);
1106 }
1107
1108 return ret;
1109}
1110
Michal Vasko428087d2016-01-14 16:04:28 +01001111API struct nc_pollsession *
1112nc_ps_new(void)
1113{
Michal Vasko48a63ed2016-03-01 09:48:21 +01001114 struct nc_pollsession *ps;
1115
1116 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +01001117 if (!ps) {
1118 ERRMEM;
1119 return NULL;
1120 }
Michal Vaskobe86fe32016-04-07 10:43:03 +02001121 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001122 pthread_mutex_init(&ps->lock, NULL);
1123
1124 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +01001125}
1126
1127API void
1128nc_ps_free(struct nc_pollsession *ps)
1129{
fanchanghu3d4e7212017-08-09 09:42:30 +08001130 uint16_t i;
1131
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001132 if (!ps) {
1133 return;
1134 }
1135
Michal Vaskobe86fe32016-04-07 10:43:03 +02001136 if (ps->queue_len) {
Michal Vasko05532772021-06-03 12:12:38 +02001137 ERR(NULL, "FATAL: Freeing a pollsession structure that is currently being worked with!");
Michal Vaskobe86fe32016-04-07 10:43:03 +02001138 }
1139
fanchanghu3d4e7212017-08-09 09:42:30 +08001140 for (i = 0; i < ps->session_count; i++) {
1141 free(ps->sessions[i]);
1142 }
1143
Michal Vasko428087d2016-01-14 16:04:28 +01001144 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001145 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001146 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001147
Michal Vasko428087d2016-01-14 16:04:28 +01001148 free(ps);
1149}
1150
1151API int
1152nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1153{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001154 uint8_t q_id;
1155
Michal Vasko45e53ae2016-04-07 11:46:03 +02001156 if (!ps) {
1157 ERRARG("ps");
1158 return -1;
1159 } else if (!session) {
1160 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001161 return -1;
1162 }
1163
Michal Vasko48a63ed2016-03-01 09:48:21 +01001164 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001165 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001166 return -1;
1167 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001168
Michal Vasko428087d2016-01-14 16:04:28 +01001169 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001170 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001171 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001172 ERRMEM;
1173 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001174 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001175 return -1;
1176 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001177 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1178 if (!ps->sessions[ps->session_count - 1]) {
1179 ERRMEM;
1180 --ps->session_count;
1181 /* UNLOCK */
1182 nc_ps_unlock(ps, q_id, __func__);
1183 return -1;
1184 }
1185 ps->sessions[ps->session_count - 1]->session = session;
1186 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001187
Michal Vasko48a63ed2016-03-01 09:48:21 +01001188 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001189 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001190}
1191
Michal Vasko48a63ed2016-03-01 09:48:21 +01001192static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001193_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001194{
1195 uint16_t i;
1196
Radek Krejcid5f978f2016-03-03 13:14:45 +01001197 if (index >= 0) {
1198 i = (uint16_t)index;
1199 goto remove;
1200 }
Michal Vasko428087d2016-01-14 16:04:28 +01001201 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001202 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001203remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001204 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001205 if (i <= ps->session_count) {
1206 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001207 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001208 }
1209 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001210 free(ps->sessions);
1211 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001212 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001213 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001214 return 0;
1215 }
1216 }
1217
Michal Vaskof0537d82016-01-29 14:42:38 +01001218 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001219}
1220
Michal Vasko48a63ed2016-03-01 09:48:21 +01001221API int
1222nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1223{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001224 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001225 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001226
Michal Vasko45e53ae2016-04-07 11:46:03 +02001227 if (!ps) {
1228 ERRARG("ps");
1229 return -1;
1230 } else if (!session) {
1231 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001232 return -1;
1233 }
1234
1235 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001236 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001237 return -1;
1238 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001239
Radek Krejcid5f978f2016-03-03 13:14:45 +01001240 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001241
1242 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001243 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001244
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001245 return ret || ret2 ? -1 : 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001246}
1247
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001248API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001249nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001250{
1251 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001252 struct nc_session *ret = NULL;
1253
1254 if (!ps) {
1255 ERRARG("ps");
1256 return NULL;
1257 }
1258
1259 /* LOCK */
1260 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1261 return NULL;
1262 }
1263
Michal Vasko4871c9d2017-10-09 14:48:39 +02001264 if (idx < ps->session_count) {
1265 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001266 }
1267
1268 /* UNLOCK */
1269 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1270
1271 return ret;
1272}
1273
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001274API uint16_t
1275nc_ps_session_count(struct nc_pollsession *ps)
1276{
Michal Vasko47003942019-03-14 12:25:23 +01001277 uint8_t q_id;
1278 uint16_t session_count;
1279
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001280 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001281 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001282 return 0;
1283 }
1284
Michal Vasko47003942019-03-14 12:25:23 +01001285 /* LOCK (just for memory barrier so that we read the current value) */
1286 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1287 return 0;
1288 }
1289
1290 session_count = ps->session_count;
1291
1292 /* UNLOCK */
1293 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1294
1295 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001296}
1297
Michal Vasko131120a2018-05-29 15:44:02 +02001298/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001299 * returns: NC_PSPOLL_ERROR,
Michal Vasko77367452021-02-16 16:32:18 +01001300 * NC_PSPOLL_TIMEOUT,
Michal Vasko71090fc2016-05-24 16:37:28 +02001301 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1302 * NC_PSPOLL_RPC
1303 */
1304static int
Michal Vasko131120a2018-05-29 15:44:02 +02001305nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001306{
Michal Vasko77367452021-02-16 16:32:18 +01001307 struct ly_in *msg;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001308 struct nc_server_reply *reply = NULL;
Michal Vasko939ffce2021-04-12 13:02:01 +02001309 struct lyd_node *e;
Michal Vasko77367452021-02-16 16:32:18 +01001310 int r, ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001311
Michal Vasko45e53ae2016-04-07 11:46:03 +02001312 if (!session) {
1313 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001314 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001315 } else if (!rpc) {
1316 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001317 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001318 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vasko05532772021-06-03 12:12:38 +02001319 ERR(session, "Invalid session to receive RPCs.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001320 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001321 }
1322
Michal Vasko93224072021-11-09 12:14:28 +01001323 *rpc = NULL;
1324
Michal Vasko77367452021-02-16 16:32:18 +01001325 /* get a message */
1326 r = nc_read_msg_io(session, io_timeout, &msg, 0);
1327 if (r == -2) {
1328 /* malformed message */
1329 ret = NC_PSPOLL_REPLY_ERROR;
Michal Vasko93224072021-11-09 12:14:28 +01001330 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vasko77367452021-02-16 16:32:18 +01001331 goto send_reply;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001332 }
1333 if (r == -1) {
Michal Vasko77367452021-02-16 16:32:18 +01001334 return NC_PSPOLL_ERROR;
1335 } else if (!r) {
1336 return NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001337 }
1338
Michal Vasko77367452021-02-16 16:32:18 +01001339 *rpc = calloc(1, sizeof **rpc);
1340 if (!*rpc) {
1341 ERRMEM;
1342 ret = NC_PSPOLL_REPLY_ERROR;
1343 goto cleanup;
1344 }
1345
1346 /* parse the RPC */
Michal Vasko93224072021-11-09 12:14:28 +01001347 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 +01001348 /* bad RPC received */
1349 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1350
1351 if ((*rpc)->envp) {
1352 /* at least the envelopes were parsed */
Michal Vasko93224072021-11-09 12:14:28 +01001353 e = nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
1354 nc_err_set_msg(e, ly_errmsg(session->ctx), "en");
Michal Vasko939ffce2021-04-12 13:02:01 +02001355 reply = nc_server_reply_err(e);
Michal Vasko77367452021-02-16 16:32:18 +01001356 } else if (session->version == NC_VERSION_11) {
Michal Vasko93224072021-11-09 12:14:28 +01001357 /* completely malformed message, NETCONF version 1.1 defines sending error reply from
1358 * the server (RFC 6241 sec. 3) */
1359 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_MALFORMED_MSG));
Michal Vasko77367452021-02-16 16:32:18 +01001360 }
1361
1362send_reply:
1363 if (reply) {
Michal Vasko93224072021-11-09 12:14:28 +01001364 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, *rpc ? (*rpc)->envp : NULL, reply);
Michal Vasko77367452021-02-16 16:32:18 +01001365 nc_server_reply_free(reply);
1366 if (r != NC_MSG_REPLY) {
Michal Vasko05532772021-06-03 12:12:38 +02001367 ERR(session, "Failed to write reply (%s), terminating session.", nc_msgtype2str[r]);
Michal Vasko77367452021-02-16 16:32:18 +01001368 if (session->status != NC_STATUS_INVALID) {
1369 session->status = NC_STATUS_INVALID;
1370 session->term_reason = NC_SESSION_TERM_OTHER;
1371 }
1372 }
1373 }
1374 } else {
1375 ret = NC_PSPOLL_RPC;
1376 }
1377
1378cleanup:
1379 ly_in_free(msg, 1);
1380 if (ret != NC_PSPOLL_RPC) {
1381 nc_server_rpc_free(*rpc);
1382 *rpc = NULL;
1383 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001384 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001385}
1386
fanchanghu966f2de2016-07-21 02:28:57 -04001387API void
1388nc_set_global_rpc_clb(nc_rpc_clb clb)
1389{
1390 global_rpc_clb = clb;
1391}
1392
Radek Krejci93e80222016-10-03 13:34:25 +02001393API NC_MSG_TYPE
1394nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1395{
Michal Vasko131120a2018-05-29 15:44:02 +02001396 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001397
1398 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001399 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001400 ERRARG("session");
1401 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001402 } else if (!notif || !notif->ntf || !notif->eventtime) {
Radek Krejci93e80222016-10-03 13:34:25 +02001403 ERRARG("notif");
1404 return NC_MSG_ERROR;
1405 }
1406
Michal Vasko131120a2018-05-29 15:44:02 +02001407 /* we do not need RPC lock for this, IO lock will be acquired properly */
1408 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001409 if (ret != NC_MSG_NOTIF) {
Michal Vasko05532772021-06-03 12:12:38 +02001410 ERR(session, "Failed to write notification (%s).", nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001411 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001412
Michal Vasko131120a2018-05-29 15:44:02 +02001413 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001414}
1415
Michal Vasko131120a2018-05-29 15:44:02 +02001416/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001417 * returns: NC_PSPOLL_ERROR,
1418 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1419 * NC_PSPOLL_REPLY_ERROR,
1420 * 0
1421 */
1422static int
Michal Vasko93224072021-11-09 12:14:28 +01001423nc_server_send_reply_io(struct nc_session *session, int io_timeout, const struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001424{
1425 nc_rpc_clb clb;
1426 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +01001427 const struct lysc_node *rpc_act = NULL;
1428 struct lyd_node *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001429 int ret = 0;
1430 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001431
Michal Vasko4a827e52016-03-03 10:59:00 +01001432 if (!rpc) {
1433 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001434 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001435 }
1436
Michal Vasko77367452021-02-16 16:32:18 +01001437 if (rpc->rpc->schema->nodetype == LYS_RPC) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001438 /* RPC */
Michal Vasko77367452021-02-16 16:32:18 +01001439 rpc_act = rpc->rpc->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001440 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001441 /* action */
Michal Vasko77367452021-02-16 16:32:18 +01001442 LYD_TREE_DFS_BEGIN(rpc->rpc, elem) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001443 if (elem->schema->nodetype == LYS_ACTION) {
1444 rpc_act = elem->schema;
1445 break;
1446 }
Michal Vasko77367452021-02-16 16:32:18 +01001447 LYD_TREE_DFS_END(rpc->rpc, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001448 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001449 if (!rpc_act) {
1450 ERRINT;
1451 return NC_PSPOLL_ERROR;
1452 }
1453 }
1454
1455 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001456 if (!global_rpc_clb) {
1457 /* no callback, reply with a not-implemented error */
Michal Vasko93224072021-11-09 12:14:28 +01001458 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 +03001459 } else {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001460 reply = global_rpc_clb(rpc->rpc, session);
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001461 }
Michal Vasko428087d2016-01-14 16:04:28 +01001462 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001463 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko77367452021-02-16 16:32:18 +01001464 reply = clb(rpc->rpc, session);
Michal Vasko428087d2016-01-14 16:04:28 +01001465 }
1466
1467 if (!reply) {
Michal Vasko93224072021-11-09 12:14:28 +01001468 reply = nc_server_reply_err(nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001469 }
Michal Vasko77367452021-02-16 16:32:18 +01001470 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->envp, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001471 if (reply->type == NC_RPL_ERROR) {
1472 ret |= NC_PSPOLL_REPLY_ERROR;
1473 }
1474 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001475
Michal Vasko131120a2018-05-29 15:44:02 +02001476 if (r != NC_MSG_REPLY) {
Michal Vasko15469492021-06-09 08:40:48 +02001477 ERR(session, "Failed to write reply (%s).", nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001478 ret |= NC_PSPOLL_ERROR;
1479 }
Michal Vasko428087d2016-01-14 16:04:28 +01001480
1481 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1482 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1483 session->status = NC_STATUS_INVALID;
1484 }
1485
Michal Vasko71090fc2016-05-24 16:37:28 +02001486 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001487}
1488
Michal Vasko131120a2018-05-29 15:44:02 +02001489/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001490 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1491 * NC_PSPOLL_ERROR, (msg filled)
1492 * NC_PSPOLL_TIMEOUT,
1493 * NC_PSPOLL_RPC (some application data available),
1494 * NC_PSPOLL_SSH_CHANNEL,
1495 * NC_PSPOLL_SSH_MSG
1496 */
1497static int
Michal Vasko131120a2018-05-29 15:44:02 +02001498nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001499{
Michal Vasko9a327362017-01-11 11:31:46 +01001500 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001501 int r, ret = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001502
Michal Vasko9a327362017-01-11 11:31:46 +01001503#ifdef NC_ENABLED_SSH
1504 struct nc_session *new;
1505#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001506
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001507 /* check timeout first */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001508 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout &&
1509 (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001510 sprintf(msg, "session idle timeout elapsed");
1511 session->status = NC_STATUS_INVALID;
1512 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1513 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1514 }
1515
Michal Vasko131120a2018-05-29 15:44:02 +02001516 r = nc_session_io_lock(session, io_timeout, __func__);
1517 if (r < 0) {
1518 sprintf(msg, "session IO lock failed to be acquired");
1519 return NC_PSPOLL_ERROR;
1520 } else if (!r) {
1521 return NC_PSPOLL_TIMEOUT;
1522 }
1523
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001524 switch (session->ti_type) {
1525#ifdef NC_ENABLED_SSH
1526 case NC_TI_LIBSSH:
1527 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001528 if (r == SSH_EOF) {
1529 sprintf(msg, "SSH channel unexpected EOF");
1530 session->status = NC_STATUS_INVALID;
1531 session->term_reason = NC_SESSION_TERM_DROPPED;
1532 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1533 } else if (r == SSH_ERROR) {
1534 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001535 session->status = NC_STATUS_INVALID;
1536 session->term_reason = NC_SESSION_TERM_OTHER;
1537 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001538 } else if (!r) {
1539 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1540 /* new SSH message */
1541 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1542 if (session->ti.libssh.next) {
1543 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001544 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel &&
1545 (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko8dcaa882017-10-19 14:28:42 +02001546 /* new NETCONF SSH channel */
1547 ret = NC_PSPOLL_SSH_CHANNEL;
1548 break;
1549 }
1550 }
1551 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001552 break;
1553 }
1554 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001555
Michal Vasko8dcaa882017-10-19 14:28:42 +02001556 /* just some SSH message */
1557 ret = NC_PSPOLL_SSH_MSG;
1558 } else {
1559 ret = NC_PSPOLL_TIMEOUT;
1560 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001561 } else {
1562 /* we have some application data */
1563 ret = NC_PSPOLL_RPC;
1564 }
1565 break;
1566#endif
1567#ifdef NC_ENABLED_TLS
1568 case NC_TI_OPENSSL:
1569 r = SSL_pending(session->ti.tls);
1570 if (!r) {
1571 /* no data pending in the SSL buffer, poll fd */
1572 pfd.fd = SSL_get_rfd(session->ti.tls);
1573 if (pfd.fd < 0) {
1574 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1575 ret = NC_PSPOLL_ERROR;
1576 break;
1577 }
1578 pfd.events = POLLIN;
1579 pfd.revents = 0;
1580 r = poll(&pfd, 1, 0);
1581
1582 if ((r < 0) && (errno != EINTR)) {
1583 sprintf(msg, "poll failed (%s)", strerror(errno));
1584 session->status = NC_STATUS_INVALID;
1585 ret = NC_PSPOLL_ERROR;
1586 } else if (r > 0) {
1587 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1588 sprintf(msg, "communication socket unexpectedly closed");
1589 session->status = NC_STATUS_INVALID;
1590 session->term_reason = NC_SESSION_TERM_DROPPED;
1591 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1592 } else if (pfd.revents & POLLERR) {
1593 sprintf(msg, "communication socket error");
1594 session->status = NC_STATUS_INVALID;
1595 session->term_reason = NC_SESSION_TERM_OTHER;
1596 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1597 } else {
1598 ret = NC_PSPOLL_RPC;
1599 }
1600 } else {
1601 ret = NC_PSPOLL_TIMEOUT;
1602 }
1603 } else {
1604 ret = NC_PSPOLL_RPC;
1605 }
1606 break;
1607#endif
1608 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001609 case NC_TI_UNIX:
1610 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001611 pfd.events = POLLIN;
1612 pfd.revents = 0;
1613 r = poll(&pfd, 1, 0);
1614
1615 if ((r < 0) && (errno != EINTR)) {
1616 sprintf(msg, "poll failed (%s)", strerror(errno));
1617 session->status = NC_STATUS_INVALID;
1618 ret = NC_PSPOLL_ERROR;
1619 } else if (r > 0) {
1620 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1621 sprintf(msg, "communication socket unexpectedly closed");
1622 session->status = NC_STATUS_INVALID;
1623 session->term_reason = NC_SESSION_TERM_DROPPED;
1624 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1625 } else if (pfd.revents & POLLERR) {
1626 sprintf(msg, "communication socket error");
1627 session->status = NC_STATUS_INVALID;
1628 session->term_reason = NC_SESSION_TERM_OTHER;
1629 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1630 } else {
1631 ret = NC_PSPOLL_RPC;
1632 }
1633 } else {
1634 ret = NC_PSPOLL_TIMEOUT;
1635 }
1636 break;
1637 case NC_TI_NONE:
1638 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1639 ret = NC_PSPOLL_ERROR;
1640 break;
1641 }
1642
Michal Vasko131120a2018-05-29 15:44:02 +02001643 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001644 return ret;
1645}
1646
1647API int
1648nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1649{
1650 int ret, r;
1651 uint8_t q_id;
1652 uint16_t i, j;
1653 char msg[256];
1654 struct timespec ts_timeout, ts_cur;
1655 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001656 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001657 struct nc_server_rpc *rpc = NULL;
1658
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001659 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001660 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001661 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001662 }
1663
Michal Vaskoade892d2017-02-22 13:40:35 +01001664 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001665 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001666 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001667 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001668
Michal Vaskoade892d2017-02-22 13:40:35 +01001669 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001670 nc_ps_unlock(ps, q_id, __func__);
1671 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001672 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001673
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001674 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001675 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001676 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001677 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001678 nc_addtimespec(&ts_timeout, timeout);
1679 }
1680
1681 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001682 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001683 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001684 if (ps->last_event_session == ps->session_count - 1) {
1685 i = j = 0;
1686 } else {
1687 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001688 }
Michal Vasko9a327362017-01-11 11:31:46 +01001689 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001690 cur_ps_session = ps->sessions[i];
1691 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001692
Michal Vasko131120a2018-05-29 15:44:02 +02001693 /* SESSION RPC LOCK */
1694 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001695 if (r == -1) {
1696 ret = NC_PSPOLL_ERROR;
1697 } else if (r == 1) {
1698 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001699 switch (cur_ps_session->state) {
1700 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001701 if (cur_session->status == NC_STATUS_RUNNING) {
1702 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001703 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001704
Michal Vasko131120a2018-05-29 15:44:02 +02001705 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001706 switch (ret) {
1707 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_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_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001710 break;
1711 case NC_PSPOLL_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001712 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001713 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001714 break;
1715 case NC_PSPOLL_TIMEOUT:
1716#ifdef NC_ENABLED_SSH
1717 case NC_PSPOLL_SSH_CHANNEL:
1718 case NC_PSPOLL_SSH_MSG:
1719#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001720 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001721 break;
1722 case NC_PSPOLL_RPC:
1723 /* let's keep the state busy, we are not done with this session */
1724 break;
1725 }
1726 } else {
1727 /* session is not fine, let the caller know */
1728 ret = NC_PSPOLL_SESSION_TERM;
1729 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1730 ret |= NC_PSPOLL_SESSION_ERROR;
1731 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001732 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001733 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001734 break;
1735 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001736 /* it definitely should not be busy because we have the lock */
1737 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001738 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001739 break;
1740 case NC_PS_STATE_INVALID:
1741 /* we got it locked, but it will be freed, let it be */
1742 ret = NC_PSPOLL_TIMEOUT;
1743 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001744 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001745
Michal Vasko131120a2018-05-29 15:44:02 +02001746 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001747 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001748 /* SESSION RPC UNLOCK */
1749 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001750 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001751 } else {
1752 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001753 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001754 }
Michal Vasko428087d2016-01-14 16:04:28 +01001755
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001756 /* something happened */
1757 if (ret != NC_PSPOLL_TIMEOUT) {
1758 break;
1759 }
1760
Michal Vasko9a327362017-01-11 11:31:46 +01001761 if (i == ps->session_count - 1) {
1762 i = 0;
1763 } else {
1764 ++i;
1765 }
1766 } while (i != j);
1767
Michal Vaskoade892d2017-02-22 13:40:35 +01001768 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001769 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001770 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001771 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001772 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001773
1774 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1775 /* final timeout */
1776 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001777 }
Michal Vasko428087d2016-01-14 16:04:28 +01001778 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001779 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001780
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001781 /* do we want to return the session? */
1782 switch (ret) {
1783 case NC_PSPOLL_RPC:
1784 case NC_PSPOLL_SESSION_TERM:
1785 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1786#ifdef NC_ENABLED_SSH
1787 case NC_PSPOLL_SSH_CHANNEL:
1788 case NC_PSPOLL_SSH_MSG:
1789#endif
1790 if (session) {
1791 *session = cur_session;
1792 }
1793 ps->last_event_session = i;
1794 break;
1795 default:
1796 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001797 }
Michal Vasko428087d2016-01-14 16:04:28 +01001798
Michal Vaskoade892d2017-02-22 13:40:35 +01001799 /* PS UNLOCK */
1800 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001801
Michal Vasko131120a2018-05-29 15:44:02 +02001802 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001803 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001804 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001805 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1806 if (cur_session->status != NC_STATUS_RUNNING) {
1807 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001808 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001809 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001810 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001811 }
1812 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001813 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001814
Michal Vasko7f1ee932018-10-11 09:41:42 +02001815 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001816 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001817 if (cur_session->status != NC_STATUS_RUNNING) {
1818 ret |= NC_PSPOLL_SESSION_TERM;
1819 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1820 ret |= NC_PSPOLL_SESSION_ERROR;
1821 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001822 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001823 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001824 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001825 }
Michal Vasko428087d2016-01-14 16:04:28 +01001826 }
Michal Vasko77367452021-02-16 16:32:18 +01001827 nc_server_rpc_free(rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001828
Michal Vasko131120a2018-05-29 15:44:02 +02001829 /* SESSION RPC UNLOCK */
1830 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001831 }
1832
Michal Vasko48a63ed2016-03-01 09:48:21 +01001833 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001834}
1835
Michal Vaskod09eae62016-02-01 10:32:52 +01001836API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001837nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001838{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001839 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001840 uint16_t i;
1841 struct nc_session *session;
1842
Michal Vasko9a25e932016-02-01 10:36:42 +01001843 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001844 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001845 return;
1846 }
1847
Michal Vasko48a63ed2016-03-01 09:48:21 +01001848 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001849 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001850 return;
1851 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001852
Michal Vasko48a63ed2016-03-01 09:48:21 +01001853 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001854 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001855 nc_session_free(ps->sessions[i]->session, data_free);
1856 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001857 }
1858 free(ps->sessions);
1859 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001860 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001861 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001862 } else {
1863 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001864 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1865 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001866 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001867 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001868 continue;
1869 }
1870
1871 ++i;
1872 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001873 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001874
1875 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001876 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001877}
1878
Michal Vasko5f352c52019-07-10 16:12:06 +02001879static int
apropp-molex4e903c32020-04-20 03:06:58 -04001880nc_get_uid(int sock, uid_t *uid)
1881{
Michal Vaskod3910912020-04-20 09:12:49 +02001882 int ret;
apropp-molex4e903c32020-04-20 03:06:58 -04001883
Michal Vaskod3910912020-04-20 09:12:49 +02001884#ifdef SO_PEERCRED
1885 struct ucred ucred;
1886 socklen_t len;
1887 len = sizeof(ucred);
1888 ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
1889 if (!ret) {
1890 *uid = ucred.uid;
1891 }
1892#else
1893 ret = getpeereid(sock, uid, NULL);
1894#endif
1895
1896 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001897 ERR(NULL, "Failed to get credentials from unix socket (%s).", strerror(errno));
Michal Vaskod3910912020-04-20 09:12:49 +02001898 return -1;
1899 }
apropp-molex4e903c32020-04-20 03:06:58 -04001900 return 0;
1901}
1902
1903static int
Michal Vasko5f352c52019-07-10 16:12:06 +02001904nc_accept_unix(struct nc_session *session, int sock)
1905{
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001906#if defined (SO_PEERCRED) || defined (HAVE_GETPEEREID)
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001907 struct passwd *pw, pw_buf;
Michal Vasko5f352c52019-07-10 16:12:06 +02001908 char *username;
Michal Vasko5f352c52019-07-10 16:12:06 +02001909 session->ti_type = NC_TI_UNIX;
Michal Vasko143aa142021-10-01 15:31:48 +02001910 uid_t uid = 0;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001911 char *buf = NULL;
1912 size_t buf_len = 0;
Michal Vasko5f352c52019-07-10 16:12:06 +02001913
Michal Vaskod3910912020-04-20 09:12:49 +02001914 if (nc_get_uid(sock, &uid)) {
1915 close(sock);
Michal Vasko5f352c52019-07-10 16:12:06 +02001916 return -1;
1917 }
1918
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001919 pw = nc_getpwuid(uid, &pw_buf, &buf, &buf_len);
Michal Vasko5f352c52019-07-10 16:12:06 +02001920 if (pw == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001921 ERR(NULL, "Failed to find username for uid=%u (%s).\n", uid, strerror(errno));
Michal Vasko5f352c52019-07-10 16:12:06 +02001922 close(sock);
1923 return -1;
1924 }
1925
1926 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001927 free(buf);
Michal Vasko5f352c52019-07-10 16:12:06 +02001928 if (username == NULL) {
1929 ERRMEM;
1930 close(sock);
1931 return -1;
1932 }
Michal Vasko93224072021-11-09 12:14:28 +01001933 session->username = username;
Michal Vasko5f352c52019-07-10 16:12:06 +02001934
1935 session->ti.unixsock.sock = sock;
1936
1937 return 1;
Claus Klein22091912020-01-20 13:45:47 +01001938#else
1939 return -1;
1940#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02001941}
1942
Michal Vaskoe2713da2016-08-22 16:06:40 +02001943API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001944nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001945{
Michal Vasko3031aae2016-01-27 16:07:18 +01001946 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001947 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001948
Michal Vasko45e53ae2016-04-07 11:46:03 +02001949 if (!name) {
1950 ERRARG("name");
1951 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001952 }
1953
Michal Vaskoade892d2017-02-22 13:40:35 +01001954 /* BIND LOCK */
1955 pthread_mutex_lock(&server_opts.bind_lock);
1956
1957 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001958 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001959
1960 /* check name uniqueness */
1961 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001962 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02001963 ERR(NULL, "Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001964 ret = -1;
1965 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001966 }
1967 }
1968
Michal Vasko93224072021-11-09 12:14:28 +01001969 server_opts.endpts = nc_realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001970 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001971 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001972 ret = -1;
1973 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001974 }
Michal Vasko93224072021-11-09 12:14:28 +01001975 memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts);
1976 ++server_opts.endpt_count;
1977
1978 server_opts.endpts[server_opts.endpt_count - 1].name = strdup(name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001979 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001980 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
1981 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
1982 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001983
Michal Vaskoe2713da2016-08-22 16:06:40 +02001984 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001985 if (!server_opts.binds) {
1986 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001987 ret = -1;
1988 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001989 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001990
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001991 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001992 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1993
1994 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001995#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001996 case NC_TI_LIBSSH:
1997 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1998 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1999 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002000 ret = -1;
2001 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002002 }
2003 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
Michal Vasko77367452021-02-16 16:32:18 +01002004 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002005 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002006 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002007 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002008#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02002009#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002010 case NC_TI_OPENSSL:
2011 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2012 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
2013 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01002014 ret = -1;
2015 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002016 }
2017 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002018#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002019 case NC_TI_UNIX:
2020 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
2021 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
2022 ERRMEM;
2023 ret = -1;
2024 goto cleanup;
2025 }
2026 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
2027 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
2028 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
2029 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002030 default:
2031 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01002032 ret = -1;
2033 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002034 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002035
Michal Vaskoade892d2017-02-22 13:40:35 +01002036cleanup:
2037 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002038 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002039
Michal Vaskoade892d2017-02-22 13:40:35 +01002040 /* BIND UNLOCK */
2041 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002042
Michal Vaskoade892d2017-02-22 13:40:35 +01002043 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002044}
2045
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002046API int
Michal Vasko59050372016-11-22 14:33:55 +01002047nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01002048{
2049 uint32_t i;
2050 int ret = -1;
2051
Michal Vaskoade892d2017-02-22 13:40:35 +01002052 /* BIND LOCK */
2053 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002054
Michal Vaskoade892d2017-02-22 13:40:35 +01002055 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002056 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002057
Michal Vasko59050372016-11-22 14:33:55 +01002058 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002059 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01002060 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002061 free(server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002062 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002063#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002064 case NC_TI_LIBSSH:
2065 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2066 free(server_opts.endpts[i].opts.ssh);
2067 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002068#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002069#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002070 case NC_TI_OPENSSL:
2071 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2072 free(server_opts.endpts[i].opts.tls);
2073 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002074#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002075 case NC_TI_UNIX:
2076 free(server_opts.endpts[i].opts.unixsock);
2077 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002078 default:
2079 ERRINT;
2080 /* won't get here ...*/
2081 break;
2082 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002083 ret = 0;
2084 }
Michal Vasko3031aae2016-01-27 16:07:18 +01002085 free(server_opts.endpts);
2086 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002087
2088 /* remove all binds */
2089 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002090 free(server_opts.binds[i].address);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002091 if (server_opts.binds[i].sock > -1) {
2092 close(server_opts.binds[i].sock);
2093 }
2094 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002095 free(server_opts.binds);
2096 server_opts.binds = NULL;
2097
Michal Vasko3031aae2016-01-27 16:07:18 +01002098 server_opts.endpt_count = 0;
2099
Michal Vasko1a38c862016-01-15 15:50:07 +01002100 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002101 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002102 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002103 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002104 /* remove endpt */
Michal Vasko93224072021-11-09 12:14:28 +01002105 free(server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002106 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002107#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002108 case NC_TI_LIBSSH:
2109 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2110 free(server_opts.endpts[i].opts.ssh);
2111 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002112#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002113#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002114 case NC_TI_OPENSSL:
2115 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2116 free(server_opts.endpts[i].opts.tls);
2117 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002118#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002119 case NC_TI_UNIX:
2120 free(server_opts.endpts[i].opts.unixsock);
2121 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002122 default:
2123 ERRINT;
2124 break;
2125 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002126
Michal Vaskoe2713da2016-08-22 16:06:40 +02002127 /* remove bind(s) */
Michal Vasko93224072021-11-09 12:14:28 +01002128 free(server_opts.binds[i].address);
Michal Vaskoe2713da2016-08-22 16:06:40 +02002129 if (server_opts.binds[i].sock > -1) {
2130 close(server_opts.binds[i].sock);
2131 }
2132
2133 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002134 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002135 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002136 free(server_opts.binds);
2137 server_opts.binds = NULL;
2138 free(server_opts.endpts);
2139 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002140 } else if (i < server_opts.endpt_count) {
2141 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2142 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002143 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002144
2145 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002146 if (name) {
2147 break;
2148 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002149 }
2150 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002151 }
2152
Michal Vaskoade892d2017-02-22 13:40:35 +01002153 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002154 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002155
Michal Vaskoade892d2017-02-22 13:40:35 +01002156 /* BIND UNLOCK */
2157 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002158
2159 return ret;
2160}
2161
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002162API int
2163nc_server_endpt_count(void)
2164{
2165 return server_opts.endpt_count;
2166}
2167
Michal Vasko1b5973e2020-01-30 16:05:46 +01002168API int
2169nc_server_is_endpt(const char *name)
2170{
2171 uint16_t i;
2172 int found = 0;
2173
Michal Vaskofb1724b2020-01-31 11:02:00 +01002174 if (!name) {
2175 return found;
2176 }
2177
Michal Vasko1b5973e2020-01-30 16:05:46 +01002178 /* ENDPT READ LOCK */
2179 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2180
2181 /* check name uniqueness */
2182 for (i = 0; i < server_opts.endpt_count; ++i) {
2183 if (!strcmp(server_opts.endpts[i].name, name)) {
2184 found = 1;
2185 break;
2186 }
2187 }
2188
2189 /* ENDPT UNLOCK */
2190 pthread_rwlock_unlock(&server_opts.endpt_lock);
2191
2192 return found;
2193}
2194
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002195int
2196nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
2197{
2198 struct nc_endpt *endpt;
2199 struct nc_bind *bind = NULL;
2200 uint16_t i;
2201 int sock = -1, set_addr, ret = 0;
2202
2203 if (!endpt_name) {
2204 ERRARG("endpt_name");
2205 return -1;
2206 } else if ((!address && !port) || (address && port)) {
2207 ERRARG("address and port");
2208 return -1;
2209 }
2210
2211 if (address) {
2212 set_addr = 1;
2213 } else {
2214 set_addr = 0;
2215 }
2216
2217 /* BIND LOCK */
2218 pthread_mutex_lock(&server_opts.bind_lock);
2219
2220 /* ENDPT LOCK */
2221 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2222 if (!endpt) {
2223 /* BIND UNLOCK */
2224 pthread_mutex_unlock(&server_opts.bind_lock);
2225 return -1;
2226 }
2227
2228 bind = &server_opts.binds[i];
2229
2230 if (set_addr) {
2231 port = bind->port;
2232 } else {
2233 address = bind->address;
2234 }
2235
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002236 if (!set_addr && (endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002237 ret = -1;
2238 goto cleanup;
2239 }
2240
2241 /* we have all the information we need to create a listening socket */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002242 if (address && (port || (endpt->ti == NC_TI_UNIX))) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002243 /* create new socket, close the old one */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002244 if (endpt->ti == NC_TI_UNIX) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002245 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002246 } else {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002247 sock = nc_sock_listen_inet(address, port, &endpt->ka);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002248 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002249 if (sock == -1) {
2250 ret = -1;
2251 goto cleanup;
2252 }
2253
2254 if (bind->sock > -1) {
2255 close(bind->sock);
2256 }
2257 bind->sock = sock;
2258 } /* else we are just setting address or port */
2259
2260 if (set_addr) {
Michal Vasko93224072021-11-09 12:14:28 +01002261 free(bind->address);
2262 bind->address = strdup(address);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002263 } else {
2264 bind->port = port;
2265 }
2266
2267 if (sock > -1) {
Michal Vasko946cacb2020-08-12 11:18:08 +02002268 switch (endpt->ti) {
2269 case NC_TI_UNIX:
Michal Vasko05532772021-06-03 12:12:38 +02002270 VRB(NULL, "Listening on %s for UNIX connections.", address);
Michal Vasko946cacb2020-08-12 11:18:08 +02002271 break;
2272#ifdef NC_ENABLED_SSH
2273 case NC_TI_LIBSSH:
Michal Vasko05532772021-06-03 12:12:38 +02002274 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002275 break;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002276#endif
Michal Vasko946cacb2020-08-12 11:18:08 +02002277#ifdef NC_ENABLED_TLS
2278 case NC_TI_OPENSSL:
Michal Vasko05532772021-06-03 12:12:38 +02002279 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002280 break;
2281#endif
2282 default:
2283 ERRINT;
2284 break;
2285 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002286 }
2287
2288cleanup:
2289 /* ENDPT UNLOCK */
2290 pthread_rwlock_unlock(&server_opts.endpt_lock);
2291
2292 /* BIND UNLOCK */
2293 pthread_mutex_unlock(&server_opts.bind_lock);
2294
2295 return ret;
2296}
2297
2298API int
2299nc_server_endpt_set_address(const char *endpt_name, const char *address)
2300{
2301 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2302}
2303
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002304#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002305
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002306API int
2307nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2308{
2309 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2310}
2311
Michal Vasko946cacb2020-08-12 11:18:08 +02002312#endif
2313
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002314API int
2315nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2316{
2317 struct nc_endpt *endpt;
2318 uint16_t i;
2319 int ret = 0;
2320
2321 if (!endpt_name) {
2322 ERRARG("endpt_name");
2323 return -1;
2324 } else if (mode == 0) {
2325 ERRARG("mode");
2326 return -1;
2327 }
2328
2329 /* ENDPT LOCK */
2330 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002331 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002332 return -1;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002333 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002334
2335 if (endpt->ti != NC_TI_UNIX) {
2336 ret = -1;
2337 goto cleanup;
2338 }
2339
2340 endpt->opts.unixsock->mode = mode;
2341 endpt->opts.unixsock->uid = uid;
2342 endpt->opts.unixsock->gid = gid;
2343
2344cleanup:
2345 /* ENDPT UNLOCK */
2346 pthread_rwlock_unlock(&server_opts.endpt_lock);
2347
2348 return ret;
2349}
2350
2351API int
2352nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2353{
2354 struct nc_endpt *endpt;
2355 int ret = 0;
2356
2357 if (!endpt_name) {
2358 ERRARG("endpt_name");
2359 return -1;
2360 }
2361
2362 /* ENDPT LOCK */
2363 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2364 if (!endpt) {
2365 return -1;
2366 }
2367
2368 endpt->ka.enabled = (enable ? 1 : 0);
2369
2370 /* ENDPT UNLOCK */
2371 pthread_rwlock_unlock(&server_opts.endpt_lock);
2372
2373 return ret;
2374}
2375
2376API int
2377nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2378{
2379 struct nc_endpt *endpt;
2380 int ret = 0;
2381
2382 if (!endpt_name) {
2383 ERRARG("endpt_name");
2384 return -1;
2385 }
2386
2387 /* ENDPT LOCK */
2388 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2389 if (!endpt) {
2390 return -1;
2391 }
2392
2393 if (idle_time > -1) {
2394 endpt->ka.idle_time = idle_time;
2395 }
2396 if (max_probes > -1) {
2397 endpt->ka.max_probes = max_probes;
2398 }
2399 if (probe_interval > -1) {
2400 endpt->ka.probe_interval = probe_interval;
2401 }
2402
2403 /* ENDPT UNLOCK */
2404 pthread_rwlock_unlock(&server_opts.endpt_lock);
2405
2406 return ret;
2407}
2408
Michal Vasko71090fc2016-05-24 16:37:28 +02002409API NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +01002410nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002411{
Michal Vasko71090fc2016-05-24 16:37:28 +02002412 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002413 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002414 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002415 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002416 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002417
Michal Vasko93224072021-11-09 12:14:28 +01002418 if (!ctx) {
2419 ERRARG("ctx");
Michal Vasko71090fc2016-05-24 16:37:28 +02002420 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002421 } else if (!session) {
2422 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002423 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002424 }
2425
Michal Vasko93224072021-11-09 12:14:28 +01002426 /* init ctx as needed */
2427 nc_server_init_ctx(ctx);
2428
Michal Vaskoade892d2017-02-22 13:40:35 +01002429 /* BIND LOCK */
2430 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002431
2432 if (!server_opts.endpt_count) {
Michal Vasko05532772021-06-03 12:12:38 +02002433 ERR(NULL, "No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002434 /* BIND UNLOCK */
2435 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002436 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002437 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002438
Michal Vaskoe2713da2016-08-22 16:06:40 +02002439 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002440 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002441 /* BIND UNLOCK */
2442 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002443 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002444 if (!ret) {
2445 return NC_MSG_WOULDBLOCK;
2446 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002447 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002448 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002449
2450 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2451 /* ENDPT READ LOCK */
2452 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2453
2454 /* BIND UNLOCK */
2455 pthread_mutex_unlock(&server_opts.bind_lock);
2456
Michal Vaskob48aa812016-01-18 14:13:09 +01002457 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002458
Michal Vasko131120a2018-05-29 15:44:02 +02002459 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002460 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002461 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002462 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002463 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002464 msgtype = NC_MSG_ERROR;
2465 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002466 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002467 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01002468 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vasko1a38c862016-01-15 15:50:07 +01002469 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +01002470 (*session)->host = host;
Michal Vasko1a38c862016-01-15 15:50:07 +01002471 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002472
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002473 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002474#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002475 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2476 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002477 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002478 if (ret < 0) {
2479 msgtype = NC_MSG_ERROR;
2480 goto cleanup;
2481 } else if (!ret) {
2482 msgtype = NC_MSG_WOULDBLOCK;
2483 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002484 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002485 } else
2486#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002487#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002488 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2489 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002490 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002491 if (ret < 0) {
2492 msgtype = NC_MSG_ERROR;
2493 goto cleanup;
2494 } else if (!ret) {
2495 msgtype = NC_MSG_WOULDBLOCK;
2496 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002497 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002498 } else
2499#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002500 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2501 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2502 ret = nc_accept_unix(*session, sock);
2503 if (ret < 0) {
2504 msgtype = NC_MSG_ERROR;
2505 goto cleanup;
2506 }
2507 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002508 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002509 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002510 msgtype = NC_MSG_ERROR;
2511 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002512 }
2513
Michal Vasko2cc4c682016-03-01 09:16:48 +01002514 (*session)->data = NULL;
2515
Michal Vaskoade892d2017-02-22 13:40:35 +01002516 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002517 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002518
Michal Vaskob48aa812016-01-18 14:13:09 +01002519 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002520 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002521
Michal Vasko9e036d52016-01-08 10:49:26 +01002522 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002523 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002524 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002525 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002526 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002527 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002528 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002529
2530 nc_gettimespec_mono(&ts_cur);
2531 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2532 nc_gettimespec_real(&ts_cur);
2533 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002534 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002535
Michal Vasko71090fc2016-05-24 16:37:28 +02002536 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002537
Michal Vasko71090fc2016-05-24 16:37:28 +02002538cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002539 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002540 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002541
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002542 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002543 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002544 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002545}
2546
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002547#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002548
Michal Vaskoadf30f02019-06-24 09:34:47 +02002549/* client is expected to be locked */
2550static int
2551_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 +02002552{
2553 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002554 int ret = -1;
2555
2556 if (!endpt_name) {
2557 /* remove all endpoints */
2558 for (i = 0; i < client->ch_endpt_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002559 free(client->ch_endpts[i].name);
2560 free(client->ch_endpts[i].address);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002561 if (client->ch_endpts[i].sock_pending != -1) {
2562 close(client->ch_endpts[i].sock_pending);
2563 }
2564 switch (client->ch_endpts[i].ti) {
2565#ifdef NC_ENABLED_SSH
2566 case NC_TI_LIBSSH:
2567 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2568 free(client->ch_endpts[i].opts.ssh);
2569 break;
2570#endif
2571#ifdef NC_ENABLED_TLS
2572 case NC_TI_OPENSSL:
2573 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2574 free(client->ch_endpts[i].opts.tls);
2575 break;
2576#endif
2577 default:
2578 ERRINT;
2579 /* won't get here ...*/
2580 break;
2581 }
2582 }
2583 free(client->ch_endpts);
2584 client->ch_endpts = NULL;
2585 client->ch_endpt_count = 0;
2586
2587 ret = 0;
2588 } else {
2589 for (i = 0; i < client->ch_endpt_count; ++i) {
2590 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
Michal Vasko93224072021-11-09 12:14:28 +01002591 free(client->ch_endpts[i].name);
2592 free(client->ch_endpts[i].address);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002593 if (client->ch_endpts[i].sock_pending != -1) {
2594 close(client->ch_endpts[i].sock_pending);
2595 }
2596 switch (client->ch_endpts[i].ti) {
2597#ifdef NC_ENABLED_SSH
2598 case NC_TI_LIBSSH:
2599 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2600 free(client->ch_endpts[i].opts.ssh);
2601 break;
2602#endif
2603#ifdef NC_ENABLED_TLS
2604 case NC_TI_OPENSSL:
2605 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2606 free(client->ch_endpts[i].opts.tls);
2607 break;
2608#endif
2609 default:
2610 ERRINT;
2611 /* won't get here ...*/
2612 break;
2613 }
2614
2615 /* move last endpoint to the empty space */
2616 --client->ch_endpt_count;
2617 if (i < client->ch_endpt_count) {
2618 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2619 } else if (!server_opts.ch_client_count) {
2620 free(server_opts.ch_clients);
2621 server_opts.ch_clients = NULL;
2622 }
2623
2624 ret = 0;
2625 break;
2626 }
2627 }
2628 }
2629
2630 return ret;
2631}
2632
2633API int
2634nc_server_ch_add_client(const char *name)
2635{
2636 uint16_t i;
2637 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002638
2639 if (!name) {
2640 ERRARG("name");
2641 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002642 }
2643
2644 /* WRITE LOCK */
2645 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2646
2647 /* check name uniqueness */
2648 for (i = 0; i < server_opts.ch_client_count; ++i) {
2649 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002650 ERR(NULL, "Call Home client \"%s\" already exists.", name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002651 /* WRITE UNLOCK */
2652 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2653 return -1;
2654 }
2655 }
2656
2657 ++server_opts.ch_client_count;
2658 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2659 if (!server_opts.ch_clients) {
2660 ERRMEM;
2661 /* WRITE UNLOCK */
2662 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2663 return -1;
2664 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002665 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002666
Michal Vasko93224072021-11-09 12:14:28 +01002667 client->name = strdup(name);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002668 client->id = ATOMIC_INC_RELAXED(server_opts.new_client_id);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002669 client->ch_endpts = NULL;
2670 client->ch_endpt_count = 0;
2671 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002672
Michal Vasko2e6defd2016-10-07 15:48:15 +02002673 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002674 client->start_with = NC_CH_FIRST_LISTED;
2675 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002676
Michal Vaskoadf30f02019-06-24 09:34:47 +02002677 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002678
2679 /* WRITE UNLOCK */
2680 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2681
2682 return 0;
2683}
2684
2685API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002686nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002687{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002688 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002689 int ret = -1;
2690
2691 /* WRITE LOCK */
2692 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2693
Michal Vaskoadf30f02019-06-24 09:34:47 +02002694 if (!name) {
2695 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002696 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +01002697 free(server_opts.ch_clients[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002698
2699 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002700 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002701
2702 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002703 ret = 0;
2704 }
2705 free(server_opts.ch_clients);
2706 server_opts.ch_clients = NULL;
2707
2708 server_opts.ch_client_count = 0;
2709
2710 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002711 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002712 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002713 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko93224072021-11-09 12:14:28 +01002714 free(server_opts.ch_clients[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002715
Michal Vasko2e6defd2016-10-07 15:48:15 +02002716 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002717 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002718
2719 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2720
2721 /* move last client and endpoint(s) to the empty space */
2722 --server_opts.ch_client_count;
2723 if (i < server_opts.ch_client_count) {
2724 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002725 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002726 } else if (!server_opts.ch_client_count) {
2727 free(server_opts.ch_clients);
2728 server_opts.ch_clients = NULL;
2729 }
2730
2731 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002732 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002733 }
2734 }
2735 }
2736
2737 /* WRITE UNLOCK */
2738 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2739
2740 return ret;
2741}
2742
2743API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002744nc_server_ch_is_client(const char *name)
2745{
2746 uint16_t i;
2747 int found = 0;
2748
2749 if (!name) {
2750 return found;
2751 }
2752
2753 /* READ LOCK */
2754 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2755
2756 /* check name uniqueness */
2757 for (i = 0; i < server_opts.ch_client_count; ++i) {
2758 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2759 found = 1;
2760 break;
2761 }
2762 }
2763
2764 /* UNLOCK */
2765 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2766
2767 return found;
2768}
2769
2770API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002771nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002772{
2773 uint16_t i;
2774 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002775 struct nc_ch_endpt *endpt;
2776 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002777
2778 if (!client_name) {
2779 ERRARG("client_name");
2780 return -1;
2781 } else if (!endpt_name) {
2782 ERRARG("endpt_name");
2783 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002784 } else if (!ti) {
2785 ERRARG("ti");
2786 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002787 }
2788
2789 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002790 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002791 if (!client) {
2792 return -1;
2793 }
2794
2795 for (i = 0; i < client->ch_endpt_count; ++i) {
2796 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002797 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002798 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002799 }
2800 }
2801
2802 ++client->ch_endpt_count;
2803 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2804 if (!client->ch_endpts) {
2805 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002806 goto cleanup;
2807 }
2808 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2809
2810 memset(endpt, 0, sizeof *client->ch_endpts);
Michal Vasko93224072021-11-09 12:14:28 +01002811 endpt->name = strdup(endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002812 endpt->ti = ti;
2813 endpt->sock_pending = -1;
2814 endpt->ka.idle_time = 1;
2815 endpt->ka.max_probes = 10;
2816 endpt->ka.probe_interval = 5;
2817
2818 switch (ti) {
2819#ifdef NC_ENABLED_SSH
2820 case NC_TI_LIBSSH:
2821 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2822 if (!endpt->opts.ssh) {
2823 ERRMEM;
2824 goto cleanup;
2825 }
2826 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2827 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002828 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002829 break;
2830#endif
2831#ifdef NC_ENABLED_TLS
2832 case NC_TI_OPENSSL:
2833 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2834 if (!endpt->opts.tls) {
2835 ERRMEM;
2836 goto cleanup;
2837 }
2838 break;
2839#endif
2840 default:
2841 ERRINT;
2842 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002843 }
2844
Michal Vaskoadf30f02019-06-24 09:34:47 +02002845 /* success */
2846 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002847
Michal Vaskoadf30f02019-06-24 09:34:47 +02002848cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002849 /* UNLOCK */
2850 nc_server_ch_client_unlock(client);
2851
Michal Vaskoadf30f02019-06-24 09:34:47 +02002852 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002853}
2854
2855API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002856nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002857{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002858 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002859 struct nc_ch_client *client;
2860
2861 if (!client_name) {
2862 ERRARG("client_name");
2863 return -1;
2864 }
2865
2866 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002867 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002868 if (!client) {
2869 return -1;
2870 }
2871
Michal Vaskoadf30f02019-06-24 09:34:47 +02002872 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002873
2874 /* UNLOCK */
2875 nc_server_ch_client_unlock(client);
2876
2877 return ret;
2878}
2879
2880API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002881nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2882{
2883 uint16_t i;
2884 struct nc_ch_client *client = NULL;
2885 int found = 0;
2886
2887 if (!client_name || !endpt_name) {
2888 return found;
2889 }
2890
2891 /* READ LOCK */
2892 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2893
2894 for (i = 0; i < server_opts.ch_client_count; ++i) {
2895 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2896 client = &server_opts.ch_clients[i];
2897 break;
2898 }
2899 }
2900
2901 if (!client) {
2902 goto cleanup;
2903 }
2904
2905 for (i = 0; i < client->ch_endpt_count; ++i) {
2906 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2907 found = 1;
2908 break;
2909 }
2910 }
2911
2912cleanup:
2913 /* UNLOCK */
2914 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2915 return found;
2916}
2917
2918API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002919nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2920{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002921 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002922 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002923
2924 if (!client_name) {
2925 ERRARG("client_name");
2926 return -1;
2927 } else if (!endpt_name) {
2928 ERRARG("endpt_name");
2929 return -1;
2930 } else if (!address) {
2931 ERRARG("address");
2932 return -1;
2933 }
2934
2935 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002936 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2937 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002938 return -1;
2939 }
2940
Michal Vasko93224072021-11-09 12:14:28 +01002941 free(endpt->address);
2942 endpt->address = strdup(address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002943
2944 /* UNLOCK */
2945 nc_server_ch_client_unlock(client);
2946
Michal Vaskoadf30f02019-06-24 09:34:47 +02002947 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002948}
2949
2950API int
2951nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2952{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002953 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002954 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002955
2956 if (!client_name) {
2957 ERRARG("client_name");
2958 return -1;
2959 } else if (!endpt_name) {
2960 ERRARG("endpt_name");
2961 return -1;
2962 } else if (!port) {
2963 ERRARG("port");
2964 return -1;
2965 }
2966
2967 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002968 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2969 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002970 return -1;
2971 }
2972
Michal Vaskoadf30f02019-06-24 09:34:47 +02002973 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002974
2975 /* UNLOCK */
2976 nc_server_ch_client_unlock(client);
2977
ravsz5c5a4422020-03-31 15:53:21 +02002978 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002979}
2980
2981API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002982nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
2983{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002984 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002985 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002986
2987 if (!client_name) {
2988 ERRARG("client_name");
2989 return -1;
2990 } else if (!endpt_name) {
2991 ERRARG("endpt_name");
2992 return -1;
2993 }
2994
2995 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002996 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2997 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002998 return -1;
2999 }
3000
Michal Vaskoadf30f02019-06-24 09:34:47 +02003001 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003002
3003 /* UNLOCK */
3004 nc_server_ch_client_unlock(client);
3005
Michal Vasko9af829a2019-09-12 13:50:00 +02003006 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003007}
3008
3009API int
3010nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
3011 int probe_interval)
3012{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003013 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02003014 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003015
3016 if (!client_name) {
3017 ERRARG("client_name");
3018 return -1;
3019 } else if (!endpt_name) {
3020 ERRARG("endpt_name");
3021 return -1;
3022 }
3023
3024 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003025 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
3026 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003027 return -1;
3028 }
3029
Michal Vaskoadf30f02019-06-24 09:34:47 +02003030 if (idle_time > -1) {
3031 endpt->ka.idle_time = idle_time;
3032 }
3033 if (max_probes > -1) {
3034 endpt->ka.max_probes = max_probes;
3035 }
3036 if (probe_interval > -1) {
3037 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003038 }
3039
3040 /* UNLOCK */
3041 nc_server_ch_client_unlock(client);
3042
Michal Vasko9af829a2019-09-12 13:50:00 +02003043 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003044}
3045
3046API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003047nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
3048{
3049 struct nc_ch_client *client;
3050
3051 if (!client_name) {
3052 ERRARG("client_name");
3053 return -1;
3054 } else if (!conn_type) {
3055 ERRARG("conn_type");
3056 return -1;
3057 }
3058
3059 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003060 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003061 if (!client) {
3062 return -1;
3063 }
3064
3065 if (client->conn_type != conn_type) {
3066 client->conn_type = conn_type;
3067
3068 /* set default options */
3069 switch (conn_type) {
3070 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003071 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003072 break;
3073 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003074 client->conn.period.period = 60;
3075 client->conn.period.anchor_time = 0;
3076 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003077 break;
3078 default:
3079 ERRINT;
3080 break;
3081 }
3082 }
3083
3084 /* UNLOCK */
3085 nc_server_ch_client_unlock(client);
3086
3087 return 0;
3088}
3089
3090API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003091nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
3092{
3093 struct nc_ch_client *client;
3094
3095 if (!client_name) {
3096 ERRARG("client_name");
3097 return -1;
3098 } else if (!period) {
3099 ERRARG("period");
3100 return -1;
3101 }
3102
3103 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003104 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003105 if (!client) {
3106 return -1;
3107 }
3108
3109 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003110 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003111 /* UNLOCK */
3112 nc_server_ch_client_unlock(client);
3113 return -1;
3114 }
3115
3116 client->conn.period.period = period;
3117
3118 /* UNLOCK */
3119 nc_server_ch_client_unlock(client);
3120
3121 return 0;
3122}
3123
3124API int
3125nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003126{
3127 struct nc_ch_client *client;
3128
3129 if (!client_name) {
3130 ERRARG("client_name");
3131 return -1;
3132 }
3133
3134 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003135 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003136 if (!client) {
3137 return -1;
3138 }
3139
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003140 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003141 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003142 /* UNLOCK */
3143 nc_server_ch_client_unlock(client);
3144 return -1;
3145 }
3146
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003147 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003148
3149 /* UNLOCK */
3150 nc_server_ch_client_unlock(client);
3151
3152 return 0;
3153}
3154
3155API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003156nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003157{
3158 struct nc_ch_client *client;
3159
3160 if (!client_name) {
3161 ERRARG("client_name");
3162 return -1;
3163 }
3164
3165 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003166 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003167 if (!client) {
3168 return -1;
3169 }
3170
3171 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003172 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003173 /* UNLOCK */
3174 nc_server_ch_client_unlock(client);
3175 return -1;
3176 }
3177
3178 client->conn.period.idle_timeout = idle_timeout;
3179
3180 /* UNLOCK */
3181 nc_server_ch_client_unlock(client);
3182
3183 return 0;
3184}
3185
3186API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003187nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
3188{
3189 struct nc_ch_client *client;
3190
3191 if (!client_name) {
3192 ERRARG("client_name");
3193 return -1;
3194 }
3195
3196 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003197 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003198 if (!client) {
3199 return -1;
3200 }
3201
3202 client->start_with = start_with;
3203
3204 /* UNLOCK */
3205 nc_server_ch_client_unlock(client);
3206
3207 return 0;
3208}
3209
3210API int
3211nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
3212{
3213 struct nc_ch_client *client;
3214
3215 if (!client_name) {
3216 ERRARG("client_name");
3217 return -1;
3218 } else if (!max_attempts) {
3219 ERRARG("max_attempts");
3220 return -1;
3221 }
3222
3223 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003224 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003225 if (!client) {
3226 return -1;
3227 }
3228
3229 client->max_attempts = max_attempts;
3230
3231 /* UNLOCK */
3232 nc_server_ch_client_unlock(client);
3233
3234 return 0;
3235}
3236
3237/* client lock is expected to be held */
3238static NC_MSG_TYPE
Michal Vasko93224072021-11-09 12:14:28 +01003239nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb, void *ctx_cb_data,
3240 struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01003241{
Michal Vasko71090fc2016-05-24 16:37:28 +02003242 NC_MSG_TYPE msgtype;
Michal Vasko93224072021-11-09 12:14:28 +01003243 const struct ly_ctx *ctx;
Michal Vaskob05053d2016-01-22 16:12:06 +01003244 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02003245 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01003246 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01003247
Michal Vasko4c612cd2021-02-05 08:53:42 +01003248 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 +01003249 if (sock < 0) {
Michal Vasko0b30e452021-03-03 10:30:15 +01003250 if (endpt->sock_pending > -1) {
3251 ++endpt->sock_retries;
3252 if (endpt->sock_retries == NC_SOCKET_CH_RETRIES) {
Michal Vasko05532772021-06-03 12:12:38 +02003253 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 +01003254 close(endpt->sock_pending);
3255 endpt->sock_pending = -1;
3256 endpt->sock_retries = 0;
3257 }
Michal Vasko4c612cd2021-02-05 08:53:42 +01003258 }
Michal Vasko71090fc2016-05-24 16:37:28 +02003259 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003260 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00003261 /* no need to store the socket as pending any longer */
3262 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01003263
Michal Vasko93224072021-11-09 12:14:28 +01003264 /* acquire context */
3265 ctx = acquire_ctx_cb(ctx_cb_data);
3266 if (!ctx) {
3267 ERR(NULL, "Failed to acquire context for a new Call Home session.");
3268 close(sock);
3269 free(ip_host);
3270 return NC_MSG_ERROR;
3271 }
3272
3273 /* create session */
Michal Vasko131120a2018-05-29 15:44:02 +02003274 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01003275 if (!(*session)) {
3276 ERRMEM;
3277 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01003278 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02003279 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003280 }
3281 (*session)->status = NC_STATUS_STARTING;
Michal Vasko93224072021-11-09 12:14:28 +01003282 (*session)->ctx = (struct ly_ctx *)ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003283 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko93224072021-11-09 12:14:28 +01003284 (*session)->host = ip_host;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003285 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01003286
Michal Vaskob05053d2016-01-22 16:12:06 +01003287 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01003288#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02003289 if (endpt->ti == NC_TI_LIBSSH) {
3290 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01003291 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003292 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003293
Michal Vasko71090fc2016-05-24 16:37:28 +02003294 if (ret < 0) {
3295 msgtype = NC_MSG_ERROR;
3296 goto fail;
3297 } else if (!ret) {
3298 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003299 goto fail;
3300 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003301 } else
3302#endif
Radek Krejci53691be2016-02-22 13:58:37 +01003303#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02003304 if (endpt->ti == NC_TI_OPENSSL) {
3305 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01003306 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003307 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003308
Michal Vasko71090fc2016-05-24 16:37:28 +02003309 if (ret < 0) {
3310 msgtype = NC_MSG_ERROR;
3311 goto fail;
3312 } else if (!ret) {
3313 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003314 goto fail;
3315 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003316 } else
3317#endif
3318 {
Michal Vaskob05053d2016-01-22 16:12:06 +01003319 ERRINT;
3320 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003321 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003322 goto fail;
3323 }
3324
3325 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02003326 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003327
3328 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003329 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003330 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003331 goto fail;
3332 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003333
3334 nc_gettimespec_mono(&ts_cur);
3335 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
3336 nc_gettimespec_real(&ts_cur);
3337 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003338 (*session)->status = NC_STATUS_RUNNING;
3339
Michal Vasko71090fc2016-05-24 16:37:28 +02003340 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003341
3342fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003343 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003344 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02003345 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003346}
3347
Michal Vasko2e6defd2016-10-07 15:48:15 +02003348struct nc_ch_client_thread_arg {
3349 char *client_name;
Michal Vasko93224072021-11-09 12:14:28 +01003350 nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb;
3351 nc_server_ch_session_release_ctx_cb release_ctx_cb;
3352 void *ctx_cb_data;
3353 nc_server_ch_new_session_cb new_session_cb;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003354};
3355
3356static struct nc_ch_client *
3357nc_server_ch_client_with_endpt_lock(const char *name)
3358{
3359 struct nc_ch_client *client;
3360
3361 while (1) {
3362 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003363 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003364 if (!client) {
3365 return NULL;
3366 }
3367 if (client->ch_endpt_count) {
3368 return client;
3369 }
3370 /* no endpoints defined yet */
3371
3372 /* UNLOCK */
3373 nc_server_ch_client_unlock(client);
3374
3375 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3376 }
3377
3378 return NULL;
3379}
3380
3381static int
3382nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3383{
Michal Vasko3f05a092018-03-13 10:39:49 +01003384 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003385 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003386 struct timespec ts;
3387 struct nc_ch_client *client;
3388
Michal Vasko2e6defd2016-10-07 15:48:15 +02003389 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003390 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003391
Michal Vasko0db3db52021-03-03 10:45:42 +01003392 session->flags |= NC_SESSION_CALLHOME;
3393
Michal Vasko2e6defd2016-10-07 15:48:15 +02003394 /* give the session to the user */
Michal Vasko93224072021-11-09 12:14:28 +01003395 if (data->new_session_cb(data->client_name, session)) {
Michal Vaskof1c26c22021-04-12 16:34:33 +02003396 /* something is wrong, free the session */
3397 session->flags &= ~NC_SESSION_CALLHOME;
3398
3399 /* CH UNLOCK */
3400 pthread_mutex_unlock(&session->opts.server.ch_lock);
3401
3402 nc_session_free(session, NULL);
Michal Vasko93224072021-11-09 12:14:28 +01003403 goto release_ctx;
Michal Vaskof1c26c22021-04-12 16:34:33 +02003404 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003405
3406 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003407 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003408 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003409
Michal Vasko0db3db52021-03-03 10:45:42 +01003410 /* CH COND WAIT */
Michal Vaskoacf98472021-02-04 15:33:57 +01003411 r = pthread_cond_timedwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, &ts);
Michal Vasko3f05a092018-03-13 10:39:49 +01003412 if (!r) {
3413 /* we were woken up, something probably happened */
3414 if (session->status != NC_STATUS_RUNNING) {
3415 break;
3416 }
3417 } else if (r != ETIMEDOUT) {
Michal Vasko05532772021-06-03 12:12:38 +02003418 ERR(session, "Pthread condition timedwait failed (%s).", strerror(r));
Michal Vasko3f05a092018-03-13 10:39:49 +01003419 ret = -1;
3420 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003421 }
3422
Michal Vasko2e6defd2016-10-07 15:48:15 +02003423 /* check whether the client was not removed */
3424 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003425 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003426 if (!client) {
3427 /* client was removed, finish thread */
Michal Vasko05532772021-06-03 12:12:38 +02003428 VRB(session, "Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003429 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003430 ret = 1;
3431 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003432 }
3433
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003434 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003435 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003436 } else {
3437 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003438 }
3439
Michal Vasko9fb42272017-10-05 13:50:05 +02003440 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003441 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 +02003442 VRB(session, "Call Home client \"%s\": session idle timeout elapsed.", client->name);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003443 session->status = NC_STATUS_INVALID;
3444 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3445 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003446
3447 /* UNLOCK */
3448 nc_server_ch_client_unlock(client);
3449
3450 } while (session->status == NC_STATUS_RUNNING);
3451
Michal Vasko0db3db52021-03-03 10:45:42 +01003452 /* signal to nc_session_free() that CH registered this session not being valid anymore */
3453 session->flags &= ~NC_SESSION_CALLHOME;
3454
Michal Vasko27377422018-03-15 08:59:35 +01003455 /* CH UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003456 pthread_mutex_unlock(&session->opts.server.ch_lock);
Michal Vasko27377422018-03-15 08:59:35 +01003457
Michal Vasko93224072021-11-09 12:14:28 +01003458release_ctx:
3459 /* session terminated, release its context */
3460 data->release_ctx_cb(data->ctx_cb_data);
3461
Michal Vasko3f05a092018-03-13 10:39:49 +01003462 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003463}
3464
3465static void *
3466nc_ch_client_thread(void *arg)
3467{
3468 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3469 NC_MSG_TYPE msgtype;
3470 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003471 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003472 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003473 struct nc_ch_endpt *cur_endpt;
3474 struct nc_session *session;
3475 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003476 uint32_t client_id;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003477 time_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003478
3479 /* LOCK */
3480 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3481 if (!client) {
3482 goto cleanup;
3483 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003484 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003485
3486 cur_endpt = &client->ch_endpts[0];
3487 cur_endpt_name = strdup(cur_endpt->name);
3488
Michal Vasko05532772021-06-03 12:12:38 +02003489 VRB(NULL, "Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003490 while (1) {
Michal Vasko93224072021-11-09 12:14:28 +01003491 msgtype = nc_connect_ch_endpt(cur_endpt, data->acquire_ctx_cb, data->ctx_cb_data, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003492
3493 if (msgtype == NC_MSG_HELLO) {
3494 /* UNLOCK */
3495 nc_server_ch_client_unlock(client);
3496
Michal Vasko05532772021-06-03 12:12:38 +02003497 VRB(NULL, "Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003498 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3499 goto cleanup;
3500 }
Michal Vasko05532772021-06-03 12:12:38 +02003501 VRB(NULL, "Call Home client \"%s\" session terminated.", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003502
3503 /* LOCK */
3504 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3505 if (!client) {
3506 goto cleanup;
3507 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003508 if (client->id != client_id) {
3509 nc_server_ch_client_unlock(client);
3510 goto cleanup;
3511 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003512
3513 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003514 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003515 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko18e1fa02021-11-29 09:02:05 +01003516 if (client->conn.period.anchor_time) {
3517 /* anchored */
3518 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
3519 } else {
3520 /* fixed timeout */
3521 reconnect_in = client->conn.period.period * 60;
3522 }
3523
Michal Vasko2e6defd2016-10-07 15:48:15 +02003524 /* UNLOCK */
3525 nc_server_ch_client_unlock(client);
3526
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003527 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
Michal Vasko05532772021-06-03 12:12:38 +02003528 VRB(NULL, "Call Home client \"%s\" reconnecting in %d seconds.", data->client_name, reconnect_in);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003529 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003530
3531 /* LOCK */
3532 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3533 if (!client) {
3534 goto cleanup;
3535 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003536 if (client->id != client_id) {
3537 nc_server_ch_client_unlock(client);
3538 goto cleanup;
3539 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003540 }
3541
3542 /* set next endpoint to try */
3543 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003544 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003545 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003546 /* we keep the current one but due to unlock/lock we have to find it again */
3547 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3548 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3549 break;
3550 }
3551 }
3552 if (next_endpt_index >= client->ch_endpt_count) {
3553 /* endpoint was removed, start with the first one */
3554 next_endpt_index = 0;
3555 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003556 } else {
3557 /* just get a random index */
3558 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003559 }
3560
Michal Vasko2e6defd2016-10-07 15:48:15 +02003561 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003562 /* UNLOCK */
3563 nc_server_ch_client_unlock(client);
3564
Michal Vasko2e6defd2016-10-07 15:48:15 +02003565 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003566 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3567
Michal Vasko6bb116b2016-10-26 13:53:46 +02003568 /* LOCK */
3569 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3570 if (!client) {
3571 goto cleanup;
3572 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003573 if (client->id != client_id) {
3574 nc_server_ch_client_unlock(client);
3575 goto cleanup;
3576 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003577
Michal Vasko2e6defd2016-10-07 15:48:15 +02003578 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003579
3580 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003581 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3582 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003583 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003584 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003585 }
3586
Peter Feiged05f2252018-09-03 08:09:47 +00003587 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003588 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003589 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003590 cur_attempts = 0;
3591 } else if (cur_attempts == client->max_attempts) {
3592 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003593 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003594 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003595 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003596 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003597 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003598 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003599 }
3600
3601 cur_attempts = 0;
3602 } /* else we keep the current one */
3603 }
Peter Feiged05f2252018-09-03 08:09:47 +00003604
3605 cur_endpt = &client->ch_endpts[next_endpt_index];
3606 free(cur_endpt_name);
3607 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003608 }
3609
3610cleanup:
Michal Vasko05532772021-06-03 12:12:38 +02003611 VRB(NULL, "Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003612 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003613 free(data->client_name);
3614 free(data);
3615 return NULL;
3616}
3617
3618API int
Michal Vasko93224072021-11-09 12:14:28 +01003619nc_connect_ch_client_dispatch(const char *client_name, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
3620 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 +01003621{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003622 int ret;
3623 pthread_t tid;
3624 struct nc_ch_client_thread_arg *arg;
3625
3626 if (!client_name) {
3627 ERRARG("client_name");
3628 return -1;
Michal Vasko93224072021-11-09 12:14:28 +01003629 } else if (!acquire_ctx_cb) {
3630 ERRARG("acquire_ctx_cb");
3631 return -1;
3632 } else if (!release_ctx_cb) {
3633 ERRARG("release_ctx_cb");
3634 return -1;
3635 } else if (!new_session_cb) {
3636 ERRARG("new_session_cb");
Michal Vasko2e6defd2016-10-07 15:48:15 +02003637 return -1;
3638 }
3639
3640 arg = malloc(sizeof *arg);
3641 if (!arg) {
3642 ERRMEM;
3643 return -1;
3644 }
3645 arg->client_name = strdup(client_name);
3646 if (!arg->client_name) {
3647 ERRMEM;
3648 free(arg);
3649 return -1;
3650 }
Michal Vasko93224072021-11-09 12:14:28 +01003651 arg->acquire_ctx_cb = acquire_ctx_cb;
3652 arg->release_ctx_cb = release_ctx_cb;
3653 arg->ctx_cb_data = ctx_cb_data;
3654 arg->new_session_cb = new_session_cb;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003655
3656 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3657 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02003658 ERR(NULL, "Creating a new thread failed (%s).", strerror(ret));
Michal Vasko2e6defd2016-10-07 15:48:15 +02003659 free(arg->client_name);
3660 free(arg);
3661 return -1;
3662 }
3663 /* the thread now manages arg */
3664
3665 pthread_detach(tid);
3666
3667 return 0;
3668}
3669
Radek Krejci53691be2016-02-22 13:58:37 +01003670#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003671
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003672API time_t
3673nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003674{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003675 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003676 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003677 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003678 }
3679
Michal Vasko2e6defd2016-10-07 15:48:15 +02003680 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003681}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003682
3683API void
Michal Vasko71dbd772021-03-23 14:08:37 +01003684nc_session_inc_notif_status(struct nc_session *session)
Michal Vasko3486a7c2017-03-03 13:28:07 +01003685{
3686 if (!session || (session->side != NC_SERVER)) {
3687 ERRARG("session");
3688 return;
3689 }
3690
Michal Vasko71dbd772021-03-23 14:08:37 +01003691 ++session->opts.server.ntf_status;
3692}
3693
3694API void
3695nc_session_dec_notif_status(struct nc_session *session)
3696{
3697 if (!session || (session->side != NC_SERVER)) {
3698 ERRARG("session");
3699 return;
3700 }
3701
3702 if (session->opts.server.ntf_status) {
3703 --session->opts.server.ntf_status;
3704 }
Michal Vasko3486a7c2017-03-03 13:28:07 +01003705}
3706
3707API int
3708nc_session_get_notif_status(const struct nc_session *session)
3709{
3710 if (!session || (session->side != NC_SERVER)) {
3711 ERRARG("session");
3712 return 0;
3713 }
3714
3715 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003716}
Michal Vasko8f430592019-02-26 08:32:54 +01003717
3718API int
3719nc_session_is_callhome(const struct nc_session *session)
3720{
3721 if (!session || (session->side != NC_SERVER)) {
3722 ERRARG("session");
3723 return 0;
3724 }
3725
3726 if (session->flags & NC_SESSION_CALLHOME) {
3727 return 1;
3728 }
3729
3730 return 0;
3731}