blob: 239c7cf6745e9d3b8ab8cdc59f514c5a146b2d9f [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 server session manipulation functions
5 *
Michal Vasko18aeb5d2017-02-17 09:23:56 +01006 * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01007 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
Olivier Matzac7fa2f2018-10-11 10:02:04 +020014#define _GNU_SOURCE /* signals, threads, SO_PEERCRED */
Michal Vasko086311b2016-01-08 09:53:11 +010015
16#include <stdint.h>
17#include <stdlib.h>
18#include <errno.h>
19#include <string.h>
20#include <poll.h>
21#include <sys/types.h>
22#include <sys/socket.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020023#include <sys/un.h>
24#include <netinet/in.h>
25#include <netinet/tcp.h>
Michal Vasko086311b2016-01-08 09:53:11 +010026#include <arpa/inet.h>
27#include <unistd.h>
Michal Vasko0190bc32016-03-02 15:47:49 +010028#include <fcntl.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010029#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010030#include <time.h>
Michal Vaskoade892d2017-02-22 13:40:35 +010031#include <signal.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020032#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010033
Michal Vasko1a38c862016-01-15 15:50:07 +010034#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010035#include "session_server.h"
Michal Vasko0bdf70b2019-06-24 19:20:20 +020036#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010037
Michal Vaskob48aa812016-01-18 14:13:09 +010038struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010039#ifdef NC_ENABLED_SSH
40 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
41#endif
42 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020043 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
44 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010045};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010046
fanchanghu966f2de2016-07-21 02:28:57 -040047static nc_rpc_clb global_rpc_clb = NULL;
48
Michal Vasko3031aae2016-01-27 16:07:18 +010049struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010050nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010051{
52 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010053 struct nc_endpt *endpt = NULL;
54
Michal Vaskoddce1212019-05-24 09:58:49 +020055 if (!name) {
56 ERRARG("endpt_name");
57 return NULL;
58 }
59
Michal Vaskoade892d2017-02-22 13:40:35 +010060 /* WRITE LOCK */
61 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010062
63 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020064 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010065 endpt = &server_opts.endpts[i];
66 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010067 }
68 }
69
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010070 if (!endpt) {
71 ERR("Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010072 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020073 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010074 return NULL;
75 }
76
Michal Vaskoe2713da2016-08-22 16:06:40 +020077 if (idx) {
78 *idx = i;
79 }
80
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010081 return endpt;
82}
83
Michal Vaskoadf30f02019-06-24 09:34:47 +020084struct nc_ch_endpt *
85nc_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 +010086{
Michal Vaskoadf30f02019-06-24 09:34:47 +020087 uint16_t i, j;
Michal Vasko2e6defd2016-10-07 15:48:15 +020088 struct nc_ch_client *client = NULL;
Michal Vaskoadf30f02019-06-24 09:34:47 +020089 struct nc_ch_endpt *endpt = NULL;
90
91 *client_p = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +020092
Michal Vaskoddce1212019-05-24 09:58:49 +020093 if (!name) {
94 ERRARG("client_name");
95 return NULL;
96 }
97
Michal Vasko2e6defd2016-10-07 15:48:15 +020098 /* READ LOCK */
99 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
100
101 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200102 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200103 client = &server_opts.ch_clients[i];
Michal Vaskoadf30f02019-06-24 09:34:47 +0200104 if (!endpt_name && !ti) {
105 /* return only client */
106 break;
107 }
108 for (j = 0; j < client->ch_endpt_count; ++j) {
109 if (!strcmp(client->ch_endpts[j].name, endpt_name) && (!ti || (ti == client->ch_endpts[j].ti))) {
110 endpt = &client->ch_endpts[j];
111 break;
112 }
113 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200114 break;
115 }
116 }
117
118 if (!client) {
119 ERR("Call Home client \"%s\" was not found.", name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200120
Michal Vasko2e6defd2016-10-07 15:48:15 +0200121 /* READ UNLOCK */
122 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200123 } else if (endpt_name && ti && !endpt) {
124 ERR("Call Home client \"%s\" endpoint \"%s\" was not found.", name, endpt_name);
125
126 /* READ UNLOCK */
127 pthread_rwlock_unlock(&server_opts.ch_client_lock);
128 } else {
129 /* CH CLIENT LOCK */
130 pthread_mutex_lock(&client->lock);
131
132 *client_p = client;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200133 }
134
Michal Vaskoadf30f02019-06-24 09:34:47 +0200135 return endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200136}
137
138void
139nc_server_ch_client_unlock(struct nc_ch_client *client)
140{
141 /* CH CLIENT UNLOCK */
142 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100143
144 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200145 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100146}
Michal Vasko086311b2016-01-08 09:53:11 +0100147
Michal Vasko1a38c862016-01-15 15:50:07 +0100148API void
149nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
150{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200151 if (!session) {
152 ERRARG("session");
153 return;
154 } else if (!reason) {
155 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100156 return;
157 }
158
Michal Vasko142cfea2017-08-07 10:12:11 +0200159 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
160 session->killed_by = 0;
161 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100162 session->term_reason = reason;
163}
164
Michal Vasko142cfea2017-08-07 10:12:11 +0200165API void
166nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
167{
168 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
169 ERRARG("session");
170 return;
171 } else if (!sid) {
172 ERRARG("sid");
173 return;
174 }
175
176 session->killed_by = sid;
177}
178
179API void
180nc_session_set_status(struct nc_session *session, NC_STATUS status)
181{
182 if (!session) {
183 ERRARG("session");
184 return;
185 } else if (!status) {
186 ERRARG("status");
187 return;
188 }
189
190 session->status = status;
191}
192
Michal Vasko086311b2016-01-08 09:53:11 +0100193int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200194nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100195{
Michal Vasko06c860d2018-07-09 16:08:52 +0200196 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100197 int is_ipv4, sock;
198 struct sockaddr_storage saddr;
199
200 struct sockaddr_in *saddr4;
201 struct sockaddr_in6 *saddr6;
202
203
204 if (!strchr(address, ':')) {
205 is_ipv4 = 1;
206 } else {
207 is_ipv4 = 0;
208 }
209
210 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
211 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100212 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100213 goto fail;
214 }
215
Michal Vaskobe52dc22018-10-17 09:28:17 +0200216 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200217 opt = 1;
218 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
219 ERR("Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
220 goto fail;
221 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100222 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
223 ERR("Could not set TCP_NODELAY socket option (%s).", strerror(errno));
224 goto fail;
225 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200226
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200227 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100228 goto fail;
229 }
230
231 bzero(&saddr, sizeof(struct sockaddr_storage));
232 if (is_ipv4) {
233 saddr4 = (struct sockaddr_in *)&saddr;
234
235 saddr4->sin_family = AF_INET;
236 saddr4->sin_port = htons(port);
237
238 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100239 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100240 goto fail;
241 }
242
243 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100244 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100245 goto fail;
246 }
247
248 } else {
249 saddr6 = (struct sockaddr_in6 *)&saddr;
250
251 saddr6->sin6_family = AF_INET6;
252 saddr6->sin6_port = htons(port);
253
254 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100255 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100256 goto fail;
257 }
258
259 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100260 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100261 goto fail;
262 }
263 }
264
Michal Vaskofb89d772016-01-08 12:25:35 +0100265 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100266 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100267 goto fail;
268 }
269
270 return sock;
271
272fail:
273 if (sock > -1) {
274 close(sock);
275 }
276
277 return -1;
278}
279
280int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200281nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
282{
283 struct sockaddr_un sun;
284 int sock = -1;
285
286 sock = socket(AF_UNIX, SOCK_STREAM, 0);
287 if (sock == -1) {
288 ERR("Failed to create socket (%s).", strerror(errno));
289 goto fail;
290 }
291
292 memset(&sun, 0, sizeof(sun));
293 sun.sun_family = AF_UNIX;
294 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
295
296 unlink(sun.sun_path);
297 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
298 ERR("Could not bind \"%s\" (%s).", address, strerror(errno));
299 goto fail;
300 }
301
302 if (opts->mode != (mode_t)-1) {
303 if (chmod(sun.sun_path, opts->mode) < 0) {
304 ERR("Failed to set unix socket permissions (%s).", strerror(errno));
305 goto fail;
306 }
307 }
308
309 if (opts->uid != (uid_t)-1 || opts->gid != (gid_t)-1) {
310 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
311 ERR("Failed to set unix socket uid/gid (%s).", strerror(errno));
312 goto fail;
313 }
314 }
315
316 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
317 ERR("Unable to start listening on \"%s\" (%s).", address, strerror(errno));
318 goto fail;
319 }
320
321 return sock;
322
323fail:
324 if (sock > -1) {
325 close(sock);
326 }
327
328 return -1;
329}
330
331int
Michal Vasko3031aae2016-01-27 16:07:18 +0100332nc_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 +0100333{
Michal Vaskof54cd352017-02-22 13:42:02 +0100334 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100335 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100336 struct pollfd *pfd;
337 struct sockaddr_storage saddr;
338 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100339 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100340
341 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100342 if (!pfd) {
343 ERRMEM;
344 return -1;
345 }
346
Michal Vaskoac2f6182017-01-30 14:32:03 +0100347 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200348 if (binds[i].sock < 0) {
349 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200350 continue;
351 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200352 if (binds[i].pollin) {
353 binds[i].pollin = 0;
354 /* leftover pollin */
355 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100356 break;
357 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100358 pfd[pfd_count].fd = binds[i].sock;
359 pfd[pfd_count].events = POLLIN;
360 pfd[pfd_count].revents = 0;
361
362 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100363 }
364
Michal Vasko0a3f3752016-10-13 14:58:38 +0200365 if (sock == -1) {
366 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100367 sigfillset(&sigmask);
368 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100369 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100370 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
371
Michal Vasko0a3f3752016-10-13 14:58:38 +0200372 if (!ret) {
373 /* we timeouted */
374 free(pfd);
375 return 0;
376 } else if (ret == -1) {
377 ERR("Poll failed (%s).", strerror(errno));
378 free(pfd);
379 return -1;
380 }
Michal Vasko086311b2016-01-08 09:53:11 +0100381
Michal Vaskoac2f6182017-01-30 14:32:03 +0100382 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
383 /* adjust i so that indices in binds and pfd always match */
384 while (binds[i].sock != pfd[j].fd) {
385 ++i;
386 }
387
388 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200389 --ret;
390
391 if (!ret) {
392 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100393 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200394 break;
395 } else {
396 /* just remember the event for next time */
397 binds[i].pollin = 1;
398 }
399 }
Michal Vasko086311b2016-01-08 09:53:11 +0100400 }
401 }
402 free(pfd);
403
404 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100405 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100406 return -1;
407 }
408
409 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100410 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100411 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100412 return -1;
413 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200414 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100415
Michal Vasko0190bc32016-03-02 15:47:49 +0100416 /* make the socket non-blocking */
417 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
418 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100419 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100420 return -1;
421 }
422
Michal Vasko3031aae2016-01-27 16:07:18 +0100423 if (idx) {
424 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100425 }
426
Michal Vasko086311b2016-01-08 09:53:11 +0100427 /* host was requested */
428 if (host) {
429 if (saddr.ss_family == AF_INET) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000430 *host = malloc(INET_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100431 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000432 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100433 ERR("inet_ntop failed (%s).", strerror(errno));
434 free(*host);
435 *host = NULL;
436 }
Michal Vasko086311b2016-01-08 09:53:11 +0100437
Michal Vasko4eb3c312016-03-01 14:09:37 +0100438 if (port) {
439 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
440 }
441 } else {
442 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100443 }
444 } else if (saddr.ss_family == AF_INET6) {
Jan Kundrát0f942e82018-02-14 14:52:00 +0100445 *host = malloc(INET6_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100446 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000447 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100448 ERR("inet_ntop failed (%s).", strerror(errno));
449 free(*host);
450 *host = NULL;
451 }
Michal Vasko086311b2016-01-08 09:53:11 +0100452
Michal Vasko4eb3c312016-03-01 14:09:37 +0100453 if (port) {
454 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
455 }
456 } else {
457 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100458 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200459 } else if (saddr.ss_family == AF_UNIX) {
460 *host = strdup(((struct sockaddr_un *)&saddr)->sun_path);
461 if (*host) {
462 if (port) {
463 *port = 0;
464 }
465 } else {
466 ERRMEM;
467 }
Michal Vasko086311b2016-01-08 09:53:11 +0100468 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100469 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100470 }
471 }
472
473 return ret;
474}
475
Michal Vasko05ba9df2016-01-13 14:40:27 +0100476static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100477nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100478{
479 const char *identifier = NULL, *version = NULL, *format = NULL;
480 char *model_data = NULL;
481 const struct lys_module *module;
482 struct nc_server_error *err;
483 struct lyd_node *child, *data = NULL;
Michal Vasko88639e92017-08-03 14:38:10 +0200484 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100485
486 LY_TREE_FOR(rpc->child, child) {
487 if (!strcmp(child->schema->name, "identifier")) {
488 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
489 } else if (!strcmp(child->schema->name, "version")) {
490 version = ((struct lyd_node_leaf_list *)child)->value_str;
Radek Krejci1afa7792017-03-26 11:24:16 -0500491 if (version && version[0] == '\0') {
492 version = NULL;
493 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100494 } else if (!strcmp(child->schema->name, "format")) {
495 format = ((struct lyd_node_leaf_list *)child)->value_str;
496 }
497 }
Michal Vaskoddce1212019-05-24 09:58:49 +0200498 VRB("Schema \"%s\" was requested.", identifier);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100499
500 /* check version */
501 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100502 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
503 nc_err_set_msg(err, "The requested version is not supported.", "en");
504 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100505 }
506
507 /* check and get module with the name identifier */
Radek Krejci3222b7d2017-09-21 16:04:30 +0200508 module = ly_ctx_get_module(server_opts.ctx, identifier, version, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100509 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200510 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
511 }
512 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100513 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
514 nc_err_set_msg(err, "The requested schema was not found.", "en");
515 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100516 }
517
518 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100519 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100520 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL, 0, 0);
Radek Krejci90fba642016-12-07 15:59:45 +0100521 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100522 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100523 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100524 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
525 nc_err_set_msg(err, "The requested format is not supported.", "en");
526 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100527 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200528 if (!model_data) {
529 ERRINT;
530 return NULL;
531 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100532
Michal Vasko88639e92017-08-03 14:38:10 +0200533 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/data", 1);
534 if (!sdata) {
535 ERRINT;
536 free(model_data);
537 return NULL;
538 }
539
Radek Krejci539efb62016-08-24 15:05:16 +0200540 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
541 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100542 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100543 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200544 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100545 return NULL;
546 }
547
Radek Krejci36dfdb32016-09-01 16:56:35 +0200548 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100549}
550
551static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100552nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100553{
Michal Vasko428087d2016-01-14 16:04:28 +0100554 session->term_reason = NC_SESSION_TERM_CLOSED;
555 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100556}
557
Michal Vasko086311b2016-01-08 09:53:11 +0100558API int
559nc_server_init(struct ly_ctx *ctx)
560{
Michal Vasko88639e92017-08-03 14:38:10 +0200561 const struct lys_node *rpc;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000562 pthread_rwlockattr_t attr;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100563
Michal Vasko086311b2016-01-08 09:53:11 +0100564 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200565 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100566 return -1;
567 }
568
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100569 nc_init();
570
Michal Vasko05ba9df2016-01-13 14:40:27 +0100571 /* set default <get-schema> callback if not specified */
Michal Vasko88639e92017-08-03 14:38:10 +0200572 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
573 if (rpc && !rpc->priv) {
574 lys_set_private(rpc, nc_clb_default_get_schema);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100575 }
576
577 /* set default <close-session> callback if not specififed */
Michal Vasko88639e92017-08-03 14:38:10 +0200578 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session", 0);
579 if (rpc && !rpc->priv) {
580 lys_set_private(rpc, nc_clb_default_close_session);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100581 }
582
Michal Vasko086311b2016-01-08 09:53:11 +0100583 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100584
585 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500586 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100587
Frank Rimpler9f838b02018-07-25 06:44:03 +0000588 errno=0;
589
590 if (pthread_rwlockattr_init(&attr) == 0) {
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700591#if defined(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP)
Frank Rimpler9f838b02018-07-25 06:44:03 +0000592 if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
593 if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
594 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
595 }
596 if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
597 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
598 }
599 } else {
600 ERR("%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
601 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700602#endif
Frank Rimpler9f838b02018-07-25 06:44:03 +0000603 pthread_rwlockattr_destroy(&attr);
604 } else {
605 ERR("%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
606 }
Michal Vasko086311b2016-01-08 09:53:11 +0100607 return 0;
608}
609
Michal Vaskob48aa812016-01-18 14:13:09 +0100610API void
611nc_server_destroy(void)
612{
Radek Krejci658782b2016-12-04 22:04:55 +0100613 unsigned int i;
614
615 for (i = 0; i < server_opts.capabilities_count; i++) {
616 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
617 }
618 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200619 server_opts.capabilities = NULL;
620 server_opts.capabilities_count = 0;
621
Radek Krejci53691be2016-02-22 13:58:37 +0100622#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100623 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200624 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100625#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100626#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100627 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
628 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
629 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200630 server_opts.passwd_auth_data = NULL;
631 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100632
Michal Vasko17dfda92016-12-01 14:06:16 +0100633 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100634
635 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
636 server_opts.hostkey_data_free(server_opts.hostkey_data);
637 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200638 server_opts.hostkey_data = NULL;
639 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100640#endif
641#ifdef NC_ENABLED_TLS
642 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
643 server_opts.server_cert_data_free(server_opts.server_cert_data);
644 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200645 server_opts.server_cert_data = NULL;
646 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100647 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
648 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
649 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200650 server_opts.trusted_cert_list_data = NULL;
651 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100652#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100653 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100654}
655
Michal Vasko086311b2016-01-08 09:53:11 +0100656API int
657nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
658{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200659 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
660 ERRARG("basic_mode");
661 return -1;
662 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
663 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100664 return -1;
665 }
666
667 server_opts.wd_basic_mode = basic_mode;
668 server_opts.wd_also_supported = also_supported;
669 return 0;
670}
671
Michal Vasko1a38c862016-01-15 15:50:07 +0100672API void
Michal Vasko55f03972016-04-13 08:56:01 +0200673nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
674{
675 if (!basic_mode && !also_supported) {
676 ERRARG("basic_mode and also_supported");
677 return;
678 }
679
680 if (basic_mode) {
681 *basic_mode = server_opts.wd_basic_mode;
682 }
683 if (also_supported) {
684 *also_supported = server_opts.wd_also_supported;
685 }
686}
687
Michal Vasko55f03972016-04-13 08:56:01 +0200688API int
Radek Krejci658782b2016-12-04 22:04:55 +0100689nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200690{
Radek Krejci658782b2016-12-04 22:04:55 +0100691 const char **new;
692
693 if (!value || !value[0]) {
694 ERRARG("value must not be empty");
695 return EXIT_FAILURE;
696 }
697
698 server_opts.capabilities_count++;
699 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
700 if (!new) {
701 ERRMEM;
702 return EXIT_FAILURE;
703 }
704 server_opts.capabilities = new;
705 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
706
707 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200708}
709
Michal Vasko1a38c862016-01-15 15:50:07 +0100710API void
Michal Vasko086311b2016-01-08 09:53:11 +0100711nc_server_set_hello_timeout(uint16_t hello_timeout)
712{
Michal Vasko086311b2016-01-08 09:53:11 +0100713 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100714}
715
Michal Vasko55f03972016-04-13 08:56:01 +0200716API uint16_t
717nc_server_get_hello_timeout(void)
718{
719 return server_opts.hello_timeout;
720}
721
Michal Vasko1a38c862016-01-15 15:50:07 +0100722API void
Michal Vasko086311b2016-01-08 09:53:11 +0100723nc_server_set_idle_timeout(uint16_t idle_timeout)
724{
Michal Vasko086311b2016-01-08 09:53:11 +0100725 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100726}
727
Michal Vasko55f03972016-04-13 08:56:01 +0200728API uint16_t
729nc_server_get_idle_timeout(void)
730{
731 return server_opts.idle_timeout;
732}
733
Michal Vasko71090fc2016-05-24 16:37:28 +0200734API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100735nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100736{
Michal Vasko71090fc2016-05-24 16:37:28 +0200737 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200738 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200739
Michal Vasko45e53ae2016-04-07 11:46:03 +0200740 if (!server_opts.ctx) {
741 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200742 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200743 } else if (fdin < 0) {
744 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200745 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200746 } else if (fdout < 0) {
747 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200748 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200749 } else if (!username) {
750 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200751 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200752 } else if (!session) {
753 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200754 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100755 }
756
757 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200758 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100759 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100760 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200761 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100762 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100763 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100764
Michal Vasko086311b2016-01-08 09:53:11 +0100765 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100766 (*session)->ti_type = NC_TI_FD;
767 (*session)->ti.fd.in = fdin;
768 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100769
770 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100771 (*session)->flags = NC_SESSION_SHAREDCTX;
772 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100773
Michal Vaskob48aa812016-01-18 14:13:09 +0100774 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +0100775 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100776
Michal Vasko086311b2016-01-08 09:53:11 +0100777 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200778 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200779 if (msgtype != NC_MSG_HELLO) {
780 nc_session_free(*session, NULL);
781 *session = NULL;
782 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100783 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200784
785 nc_gettimespec_mono(&ts_cur);
786 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
787 nc_gettimespec_real(&ts_cur);
788 (*session)->opts.server.session_start = ts_cur.tv_sec;
789
Michal Vasko1a38c862016-01-15 15:50:07 +0100790 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100791
Michal Vasko71090fc2016-05-24 16:37:28 +0200792 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100793}
Michal Vasko9e036d52016-01-08 10:49:26 +0100794
Michal Vaskob30b99c2016-07-26 11:35:43 +0200795static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100796nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
797{
798 uint8_t q_last;
799
800 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
801 ERRINT;
802 return;
803 }
804
805 /* get a unique queue value (by adding 1 to the last added value, if any) */
806 if (ps->queue_len) {
807 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
808 *id = ps->queue[q_last] + 1;
809 } else {
810 *id = 0;
811 }
812
813 /* add the id into the queue */
814 ++ps->queue_len;
815 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
816 ps->queue[q_last] = *id;
817}
818
819static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200820nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
821{
Michal Vasko74c345f2018-02-07 10:37:11 +0100822 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200823
824 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100825 /* get the actual queue idx */
826 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200827
828 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100829 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200830 /* another equal value, simply cannot be */
831 ERRINT;
832 }
Michal Vaskod8340032018-02-12 14:41:00 +0100833 if (found == 2) {
834 /* move the following values */
835 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
836 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100837 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200838 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100839 if (i == 0) {
840 found = 1;
841 } else {
842 /* this is not okay, our id is in the middle of the queue */
843 found = 2;
844 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200845 }
846 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200847 if (!found) {
848 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100849 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200850 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100851
Michal Vasko103fe632018-02-12 16:37:45 +0100852 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100853 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100854 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100855 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
856 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200857}
858
Michal Vaskof04a52a2016-04-07 10:52:10 +0200859int
Michal Vasko26043172016-07-26 14:08:59 +0200860nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200861{
862 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200863 struct timespec ts;
864
Michal Vasko77a6abe2017-10-05 10:02:20 +0200865 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100866 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200867
868 /* LOCK */
869 ret = pthread_mutex_timedlock(&ps->lock, &ts);
870 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200871 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200872 return -1;
873 }
874
Michal Vasko74c345f2018-02-07 10:37:11 +0100875 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +0100876 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100877 ERR("%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +0100878 pthread_mutex_unlock(&ps->lock);
879 return -1;
880 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100881
882 /* add ourselves into the queue */
883 nc_ps_queue_add_id(ps, id);
Michal Vasko91290952019-09-27 11:30:55 +0200884 DBG("PS 0x%p TID %lu queue: added %u, head %u, lenght %u", ps, (long unsigned int)pthread_self(), *id,
885 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200886
887 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200888 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200889 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +0100890 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200891
892 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
893 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +0530894 /**
895 * This may happen when another thread releases the lock and broadcasts the condition
896 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
897 * but when actually this thread was ready for condition.
898 */
preetbhansali629dfc42018-12-17 16:04:40 +0530899 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +0530900 break;
901 }
Michal Vasko66032bc2019-01-22 15:03:12 +0100902
Michal Vasko26043172016-07-26 14:08:59 +0200903 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200904 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200905 nc_ps_queue_remove_id(ps, *id);
906 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200907 return -1;
908 }
909 }
910
Michal Vaskobe86fe32016-04-07 10:43:03 +0200911 /* UNLOCK */
912 pthread_mutex_unlock(&ps->lock);
913
914 return 0;
915}
916
Michal Vaskof04a52a2016-04-07 10:52:10 +0200917int
Michal Vasko26043172016-07-26 14:08:59 +0200918nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200919{
920 int ret;
921 struct timespec ts;
922
Michal Vasko77a6abe2017-10-05 10:02:20 +0200923 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100924 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200925
926 /* LOCK */
927 ret = pthread_mutex_timedlock(&ps->lock, &ts);
928 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200929 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200930 ret = -1;
931 }
932
Michal Vaskob30b99c2016-07-26 11:35:43 +0200933 /* we must be the first, it was our turn after all, right? */
934 if (ps->queue[ps->queue_begin] != id) {
935 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200936 /* UNLOCK */
937 if (!ret) {
938 pthread_mutex_unlock(&ps->lock);
939 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200940 return -1;
941 }
942
Michal Vaskobe86fe32016-04-07 10:43:03 +0200943 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200944 nc_ps_queue_remove_id(ps, id);
Michal Vasko91290952019-09-27 11:30:55 +0200945 DBG("PS 0x%p TID %lu queue: removed %u, head %u, lenght %u", ps, (long unsigned int)pthread_self(), id,
946 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200947
948 /* broadcast to all other threads that the queue moved */
949 pthread_cond_broadcast(&ps->cond);
950
Michal Vaskobe86fe32016-04-07 10:43:03 +0200951 /* UNLOCK */
952 if (!ret) {
953 pthread_mutex_unlock(&ps->lock);
954 }
955
956 return ret;
957}
958
Michal Vasko428087d2016-01-14 16:04:28 +0100959API struct nc_pollsession *
960nc_ps_new(void)
961{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100962 struct nc_pollsession *ps;
963
964 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100965 if (!ps) {
966 ERRMEM;
967 return NULL;
968 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200969 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100970 pthread_mutex_init(&ps->lock, NULL);
971
972 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100973}
974
975API void
976nc_ps_free(struct nc_pollsession *ps)
977{
fanchanghu3d4e7212017-08-09 09:42:30 +0800978 uint16_t i;
979
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100980 if (!ps) {
981 return;
982 }
983
Michal Vaskobe86fe32016-04-07 10:43:03 +0200984 if (ps->queue_len) {
985 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
986 }
987
fanchanghu3d4e7212017-08-09 09:42:30 +0800988 for (i = 0; i < ps->session_count; i++) {
989 free(ps->sessions[i]);
990 }
991
Michal Vasko428087d2016-01-14 16:04:28 +0100992 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100993 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200994 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100995
Michal Vasko428087d2016-01-14 16:04:28 +0100996 free(ps);
997}
998
999API int
1000nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1001{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001002 uint8_t q_id;
1003
Michal Vasko45e53ae2016-04-07 11:46:03 +02001004 if (!ps) {
1005 ERRARG("ps");
1006 return -1;
1007 } else if (!session) {
1008 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001009 return -1;
1010 }
1011
Michal Vasko48a63ed2016-03-01 09:48:21 +01001012 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001013 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001014 return -1;
1015 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001016
Michal Vasko428087d2016-01-14 16:04:28 +01001017 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001018 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001019 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001020 ERRMEM;
1021 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001022 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001023 return -1;
1024 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001025 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1026 if (!ps->sessions[ps->session_count - 1]) {
1027 ERRMEM;
1028 --ps->session_count;
1029 /* UNLOCK */
1030 nc_ps_unlock(ps, q_id, __func__);
1031 return -1;
1032 }
1033 ps->sessions[ps->session_count - 1]->session = session;
1034 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001035
Michal Vasko48a63ed2016-03-01 09:48:21 +01001036 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001037 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001038}
1039
Michal Vasko48a63ed2016-03-01 09:48:21 +01001040static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001041_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001042{
1043 uint16_t i;
1044
Radek Krejcid5f978f2016-03-03 13:14:45 +01001045 if (index >= 0) {
1046 i = (uint16_t)index;
1047 goto remove;
1048 }
Michal Vasko428087d2016-01-14 16:04:28 +01001049 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001050 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001051remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001052 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001053 if (i <= ps->session_count) {
1054 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001055 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001056 }
1057 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001058 free(ps->sessions);
1059 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001060 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001061 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001062 return 0;
1063 }
1064 }
1065
Michal Vaskof0537d82016-01-29 14:42:38 +01001066 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001067}
1068
Michal Vasko48a63ed2016-03-01 09:48:21 +01001069API int
1070nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1071{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001072 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001073 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001074
Michal Vasko45e53ae2016-04-07 11:46:03 +02001075 if (!ps) {
1076 ERRARG("ps");
1077 return -1;
1078 } else if (!session) {
1079 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001080 return -1;
1081 }
1082
1083 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001084 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001085 return -1;
1086 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001087
Radek Krejcid5f978f2016-03-03 13:14:45 +01001088 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001089
1090 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001091 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001092
Michal Vaskobe86fe32016-04-07 10:43:03 +02001093 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001094}
1095
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001096API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001097nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001098{
1099 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001100 struct nc_session *ret = NULL;
1101
1102 if (!ps) {
1103 ERRARG("ps");
1104 return NULL;
1105 }
1106
1107 /* LOCK */
1108 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1109 return NULL;
1110 }
1111
Michal Vasko4871c9d2017-10-09 14:48:39 +02001112 if (idx < ps->session_count) {
1113 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001114 }
1115
1116 /* UNLOCK */
1117 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1118
1119 return ret;
1120}
1121
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001122API uint16_t
1123nc_ps_session_count(struct nc_pollsession *ps)
1124{
Michal Vasko47003942019-03-14 12:25:23 +01001125 uint8_t q_id;
1126 uint16_t session_count;
1127
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001128 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001129 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001130 return 0;
1131 }
1132
Michal Vasko47003942019-03-14 12:25:23 +01001133 /* LOCK (just for memory barrier so that we read the current value) */
1134 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1135 return 0;
1136 }
1137
1138 session_count = ps->session_count;
1139
1140 /* UNLOCK */
1141 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1142
1143 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001144}
1145
Michal Vasko131120a2018-05-29 15:44:02 +02001146/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001147 * returns: NC_PSPOLL_ERROR,
1148 * NC_PSPOLL_BAD_RPC,
1149 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1150 * NC_PSPOLL_RPC
1151 */
1152static int
Michal Vasko131120a2018-05-29 15:44:02 +02001153nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001154{
1155 struct lyxml_elem *xml = NULL;
1156 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001157 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001158 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001159
Michal Vasko45e53ae2016-04-07 11:46:03 +02001160 if (!session) {
1161 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001162 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001163 } else if (!rpc) {
1164 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001165 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001166 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001167 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001168 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001169 }
1170
Michal Vasko131120a2018-05-29 15:44:02 +02001171 msgtype = nc_read_msg_io(session, io_timeout, &xml, 0);
Michal Vasko428087d2016-01-14 16:04:28 +01001172
1173 switch (msgtype) {
1174 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001175 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001176 if (!*rpc) {
1177 ERRMEM;
1178 goto error;
1179 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001180
Radek Krejcif93c7d42016-04-06 13:41:15 +02001181 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001182 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1183 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001184 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001185 /* parsing RPC failed */
Michal Vaskoc9970242018-02-14 16:03:35 +01001186 reply = nc_server_reply_err(nc_err_libyang(server_opts.ctx));
Michal Vasko131120a2018-05-29 15:44:02 +02001187 ret = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001188 nc_server_reply_free(reply);
1189 if (ret == -1) {
1190 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001191 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001192 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1193 } else {
1194 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001195 }
Michal Vasko428087d2016-01-14 16:04:28 +01001196 (*rpc)->root = xml;
1197 break;
1198 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001199 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001200 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001201 goto error;
1202 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001203 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001204 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001205 goto error;
1206 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001207 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001208 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001209 goto error;
1210 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001211 /* NC_MSG_ERROR,
Michal Vasko131120a2018-05-29 15:44:02 +02001212 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg_io()
Michal Vasko428087d2016-01-14 16:04:28 +01001213 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001214 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001215 break;
1216 }
1217
Michal Vasko71090fc2016-05-24 16:37:28 +02001218 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001219
1220error:
1221 /* cleanup */
1222 lyxml_free(server_opts.ctx, xml);
1223
Michal Vasko71090fc2016-05-24 16:37:28 +02001224 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001225}
1226
fanchanghu966f2de2016-07-21 02:28:57 -04001227API void
1228nc_set_global_rpc_clb(nc_rpc_clb clb)
1229{
1230 global_rpc_clb = clb;
1231}
1232
Radek Krejci93e80222016-10-03 13:34:25 +02001233API NC_MSG_TYPE
1234nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1235{
Michal Vasko131120a2018-05-29 15:44:02 +02001236 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001237
1238 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001239 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001240 ERRARG("session");
1241 return NC_MSG_ERROR;
1242 } else if (!notif || !notif->tree || !notif->eventtime) {
1243 ERRARG("notif");
1244 return NC_MSG_ERROR;
1245 }
1246
Michal Vasko131120a2018-05-29 15:44:02 +02001247 /* we do not need RPC lock for this, IO lock will be acquired properly */
1248 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
1249 if (ret == NC_MSG_ERROR) {
Radek Krejci93e80222016-10-03 13:34:25 +02001250 ERR("Session %u: failed to write notification.", session->id);
Radek Krejci93e80222016-10-03 13:34:25 +02001251 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001252
Michal Vasko131120a2018-05-29 15:44:02 +02001253 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001254}
1255
Michal Vasko131120a2018-05-29 15:44:02 +02001256/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001257 * returns: NC_PSPOLL_ERROR,
1258 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1259 * NC_PSPOLL_REPLY_ERROR,
1260 * 0
1261 */
1262static int
Michal Vasko131120a2018-05-29 15:44:02 +02001263nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001264{
1265 nc_rpc_clb clb;
1266 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001267 struct lys_node *rpc_act = NULL;
1268 struct lyd_node *next, *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001269 int ret = 0;
1270 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001271
Michal Vasko4a827e52016-03-03 10:59:00 +01001272 if (!rpc) {
1273 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001274 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001275 }
1276
Michal Vasko90e8e692016-07-13 12:27:57 +02001277 if (rpc->tree->schema->nodetype == LYS_RPC) {
1278 /* RPC */
1279 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001280 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001281 /* action */
1282 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1283 if (elem->schema->nodetype == LYS_ACTION) {
1284 rpc_act = elem->schema;
1285 break;
1286 }
1287 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001288 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001289 if (!rpc_act) {
1290 ERRINT;
1291 return NC_PSPOLL_ERROR;
1292 }
1293 }
1294
1295 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001296 if (!global_rpc_clb) {
1297 /* no callback, reply with a not-implemented error */
1298 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
1299 } else {
1300 reply = global_rpc_clb(rpc->tree, session);
1301 }
Michal Vasko428087d2016-01-14 16:04:28 +01001302 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001303 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001304 reply = clb(rpc->tree, session);
1305 }
1306
1307 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001308 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001309 }
Michal Vasko131120a2018-05-29 15:44:02 +02001310 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->root, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001311 if (reply->type == NC_RPL_ERROR) {
1312 ret |= NC_PSPOLL_REPLY_ERROR;
1313 }
1314 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001315
Michal Vasko131120a2018-05-29 15:44:02 +02001316 if (r != NC_MSG_REPLY) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001317 ERR("Session %u: failed to write reply.", session->id);
1318 ret |= NC_PSPOLL_ERROR;
1319 }
Michal Vasko428087d2016-01-14 16:04:28 +01001320
1321 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1322 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1323 session->status = NC_STATUS_INVALID;
1324 }
1325
Michal Vasko71090fc2016-05-24 16:37:28 +02001326 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001327}
1328
Michal Vasko131120a2018-05-29 15:44:02 +02001329/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001330 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1331 * NC_PSPOLL_ERROR, (msg filled)
1332 * NC_PSPOLL_TIMEOUT,
1333 * NC_PSPOLL_RPC (some application data available),
1334 * NC_PSPOLL_SSH_CHANNEL,
1335 * NC_PSPOLL_SSH_MSG
1336 */
1337static int
Michal Vasko131120a2018-05-29 15:44:02 +02001338nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001339{
Michal Vasko9a327362017-01-11 11:31:46 +01001340 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001341 int r, ret = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001342#ifdef NC_ENABLED_SSH
1343 struct nc_session *new;
1344#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001345
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001346 /* check timeout first */
1347 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001348 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001349 sprintf(msg, "session idle timeout elapsed");
1350 session->status = NC_STATUS_INVALID;
1351 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1352 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1353 }
1354
Michal Vasko131120a2018-05-29 15:44:02 +02001355 r = nc_session_io_lock(session, io_timeout, __func__);
1356 if (r < 0) {
1357 sprintf(msg, "session IO lock failed to be acquired");
1358 return NC_PSPOLL_ERROR;
1359 } else if (!r) {
1360 return NC_PSPOLL_TIMEOUT;
1361 }
1362
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001363 switch (session->ti_type) {
1364#ifdef NC_ENABLED_SSH
1365 case NC_TI_LIBSSH:
1366 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001367 if (r == SSH_EOF) {
1368 sprintf(msg, "SSH channel unexpected EOF");
1369 session->status = NC_STATUS_INVALID;
1370 session->term_reason = NC_SESSION_TERM_DROPPED;
1371 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1372 } else if (r == SSH_ERROR) {
1373 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001374 session->status = NC_STATUS_INVALID;
1375 session->term_reason = NC_SESSION_TERM_OTHER;
1376 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001377 } else if (!r) {
1378 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1379 /* new SSH message */
1380 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1381 if (session->ti.libssh.next) {
1382 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1383 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1384 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1385 /* new NETCONF SSH channel */
1386 ret = NC_PSPOLL_SSH_CHANNEL;
1387 break;
1388 }
1389 }
1390 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001391 break;
1392 }
1393 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001394
Michal Vasko8dcaa882017-10-19 14:28:42 +02001395 /* just some SSH message */
1396 ret = NC_PSPOLL_SSH_MSG;
1397 } else {
1398 ret = NC_PSPOLL_TIMEOUT;
1399 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001400 } else {
1401 /* we have some application data */
1402 ret = NC_PSPOLL_RPC;
1403 }
1404 break;
1405#endif
1406#ifdef NC_ENABLED_TLS
1407 case NC_TI_OPENSSL:
1408 r = SSL_pending(session->ti.tls);
1409 if (!r) {
1410 /* no data pending in the SSL buffer, poll fd */
1411 pfd.fd = SSL_get_rfd(session->ti.tls);
1412 if (pfd.fd < 0) {
1413 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1414 ret = NC_PSPOLL_ERROR;
1415 break;
1416 }
1417 pfd.events = POLLIN;
1418 pfd.revents = 0;
1419 r = poll(&pfd, 1, 0);
1420
1421 if ((r < 0) && (errno != EINTR)) {
1422 sprintf(msg, "poll failed (%s)", strerror(errno));
1423 session->status = NC_STATUS_INVALID;
1424 ret = NC_PSPOLL_ERROR;
1425 } else if (r > 0) {
1426 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1427 sprintf(msg, "communication socket unexpectedly closed");
1428 session->status = NC_STATUS_INVALID;
1429 session->term_reason = NC_SESSION_TERM_DROPPED;
1430 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1431 } else if (pfd.revents & POLLERR) {
1432 sprintf(msg, "communication socket error");
1433 session->status = NC_STATUS_INVALID;
1434 session->term_reason = NC_SESSION_TERM_OTHER;
1435 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1436 } else {
1437 ret = NC_PSPOLL_RPC;
1438 }
1439 } else {
1440 ret = NC_PSPOLL_TIMEOUT;
1441 }
1442 } else {
1443 ret = NC_PSPOLL_RPC;
1444 }
1445 break;
1446#endif
1447 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001448 case NC_TI_UNIX:
1449 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001450 pfd.events = POLLIN;
1451 pfd.revents = 0;
1452 r = poll(&pfd, 1, 0);
1453
1454 if ((r < 0) && (errno != EINTR)) {
1455 sprintf(msg, "poll failed (%s)", strerror(errno));
1456 session->status = NC_STATUS_INVALID;
1457 ret = NC_PSPOLL_ERROR;
1458 } else if (r > 0) {
1459 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1460 sprintf(msg, "communication socket unexpectedly closed");
1461 session->status = NC_STATUS_INVALID;
1462 session->term_reason = NC_SESSION_TERM_DROPPED;
1463 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1464 } else if (pfd.revents & POLLERR) {
1465 sprintf(msg, "communication socket error");
1466 session->status = NC_STATUS_INVALID;
1467 session->term_reason = NC_SESSION_TERM_OTHER;
1468 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1469 } else {
1470 ret = NC_PSPOLL_RPC;
1471 }
1472 } else {
1473 ret = NC_PSPOLL_TIMEOUT;
1474 }
1475 break;
1476 case NC_TI_NONE:
1477 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1478 ret = NC_PSPOLL_ERROR;
1479 break;
1480 }
1481
Michal Vasko131120a2018-05-29 15:44:02 +02001482 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001483 return ret;
1484}
1485
1486API int
1487nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1488{
1489 int ret, r;
1490 uint8_t q_id;
1491 uint16_t i, j;
1492 char msg[256];
1493 struct timespec ts_timeout, ts_cur;
1494 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001495 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001496 struct nc_server_rpc *rpc = NULL;
1497
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001498 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001499 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001500 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001501 }
1502
Michal Vaskoade892d2017-02-22 13:40:35 +01001503 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001504 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001505 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001506 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001507
Michal Vaskoade892d2017-02-22 13:40:35 +01001508 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001509 nc_ps_unlock(ps, q_id, __func__);
1510 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001511 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001512
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001513 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001514 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001515 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001516 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001517 nc_addtimespec(&ts_timeout, timeout);
1518 }
1519
1520 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001521 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001522 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001523 if (ps->last_event_session == ps->session_count - 1) {
1524 i = j = 0;
1525 } else {
1526 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001527 }
Michal Vasko9a327362017-01-11 11:31:46 +01001528 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001529 cur_ps_session = ps->sessions[i];
1530 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001531
Michal Vasko131120a2018-05-29 15:44:02 +02001532 /* SESSION RPC LOCK */
1533 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001534 if (r == -1) {
1535 ret = NC_PSPOLL_ERROR;
1536 } else if (r == 1) {
1537 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001538 switch (cur_ps_session->state) {
1539 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001540 if (cur_session->status == NC_STATUS_RUNNING) {
1541 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001542 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001543
Michal Vasko131120a2018-05-29 15:44:02 +02001544 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001545 switch (ret) {
1546 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1547 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001548 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001549 break;
1550 case NC_PSPOLL_ERROR:
1551 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001552 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001553 break;
1554 case NC_PSPOLL_TIMEOUT:
1555#ifdef NC_ENABLED_SSH
1556 case NC_PSPOLL_SSH_CHANNEL:
1557 case NC_PSPOLL_SSH_MSG:
1558#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001559 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001560 break;
1561 case NC_PSPOLL_RPC:
1562 /* let's keep the state busy, we are not done with this session */
1563 break;
1564 }
1565 } else {
1566 /* session is not fine, let the caller know */
1567 ret = NC_PSPOLL_SESSION_TERM;
1568 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1569 ret |= NC_PSPOLL_SESSION_ERROR;
1570 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001571 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001572 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001573 break;
1574 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001575 /* it definitely should not be busy because we have the lock */
1576 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001577 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001578 break;
1579 case NC_PS_STATE_INVALID:
1580 /* we got it locked, but it will be freed, let it be */
1581 ret = NC_PSPOLL_TIMEOUT;
1582 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001583 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001584
Michal Vasko131120a2018-05-29 15:44:02 +02001585 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001586 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001587 /* SESSION RPC UNLOCK */
1588 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001589 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001590 } else {
1591 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001592 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001593 }
Michal Vasko428087d2016-01-14 16:04:28 +01001594
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001595 /* something happened */
1596 if (ret != NC_PSPOLL_TIMEOUT) {
1597 break;
1598 }
1599
Michal Vasko9a327362017-01-11 11:31:46 +01001600 if (i == ps->session_count - 1) {
1601 i = 0;
1602 } else {
1603 ++i;
1604 }
1605 } while (i != j);
1606
Michal Vaskoade892d2017-02-22 13:40:35 +01001607 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001608 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001609 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001610 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001611 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001612
1613 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1614 /* final timeout */
1615 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001616 }
Michal Vasko428087d2016-01-14 16:04:28 +01001617 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001618 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001619
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001620 /* do we want to return the session? */
1621 switch (ret) {
1622 case NC_PSPOLL_RPC:
1623 case NC_PSPOLL_SESSION_TERM:
1624 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1625#ifdef NC_ENABLED_SSH
1626 case NC_PSPOLL_SSH_CHANNEL:
1627 case NC_PSPOLL_SSH_MSG:
1628#endif
1629 if (session) {
1630 *session = cur_session;
1631 }
1632 ps->last_event_session = i;
1633 break;
1634 default:
1635 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001636 }
Michal Vasko428087d2016-01-14 16:04:28 +01001637
Michal Vaskoade892d2017-02-22 13:40:35 +01001638 /* PS UNLOCK */
1639 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001640
Michal Vasko131120a2018-05-29 15:44:02 +02001641 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001642 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001643 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001644 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1645 if (cur_session->status != NC_STATUS_RUNNING) {
1646 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001647 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001648 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001649 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001650 }
1651 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001652 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001653
Michal Vasko7f1ee932018-10-11 09:41:42 +02001654 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001655 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001656 if (cur_session->status != NC_STATUS_RUNNING) {
1657 ret |= NC_PSPOLL_SESSION_TERM;
1658 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1659 ret |= NC_PSPOLL_SESSION_ERROR;
1660 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001661 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001662 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001663 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001664 }
Michal Vasko428087d2016-01-14 16:04:28 +01001665 }
Michal Vasko7f1ee932018-10-11 09:41:42 +02001666 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001667
Michal Vasko131120a2018-05-29 15:44:02 +02001668 /* SESSION RPC UNLOCK */
1669 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001670 }
1671
Michal Vasko48a63ed2016-03-01 09:48:21 +01001672 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001673}
1674
Michal Vaskod09eae62016-02-01 10:32:52 +01001675API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001676nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001677{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001678 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001679 uint16_t i;
1680 struct nc_session *session;
1681
Michal Vasko9a25e932016-02-01 10:36:42 +01001682 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001683 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001684 return;
1685 }
1686
Michal Vasko48a63ed2016-03-01 09:48:21 +01001687 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001688 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001689 return;
1690 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001691
Michal Vasko48a63ed2016-03-01 09:48:21 +01001692 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001693 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001694 nc_session_free(ps->sessions[i]->session, data_free);
1695 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001696 }
1697 free(ps->sessions);
1698 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001699 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001700 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001701 } else {
1702 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001703 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1704 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001705 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001706 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001707 continue;
1708 }
1709
1710 ++i;
1711 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001712 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001713
1714 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001715 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001716}
1717
Radek Krejci53691be2016-02-22 13:58:37 +01001718#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001719
Michal Vasko5f352c52019-07-10 16:12:06 +02001720static int
1721nc_accept_unix(struct nc_session *session, int sock)
1722{
Claus Klein22091912020-01-20 13:45:47 +01001723#ifdef SO_PEERCRED
Michal Vasko5f352c52019-07-10 16:12:06 +02001724 const struct passwd *pw;
1725 struct ucred ucred;
1726 char *username;
1727 socklen_t len;
Michal Vasko5f352c52019-07-10 16:12:06 +02001728 session->ti_type = NC_TI_UNIX;
1729
1730 len = sizeof(ucred);
1731 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
1732 ERR("Failed to get credentials from unix socket (%s).",
1733 strerror(errno));
1734 close(sock);
1735 return -1;
1736 }
1737
1738 pw = getpwuid(ucred.uid);
1739 if (pw == NULL) {
1740 ERR("Failed to find username for uid=%u (%s).\n", ucred.uid,
1741 strerror(errno));
1742 close(sock);
1743 return -1;
1744 }
1745
1746 username = strdup(pw->pw_name);
1747 if (username == NULL) {
1748 ERRMEM;
1749 close(sock);
1750 return -1;
1751 }
1752 session->username = lydict_insert_zc(server_opts.ctx, username);
1753
1754 session->ti.unixsock.sock = sock;
1755
1756 return 1;
Claus Klein22091912020-01-20 13:45:47 +01001757#else
1758 return -1;
1759#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02001760}
1761
Michal Vaskoe2713da2016-08-22 16:06:40 +02001762API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001763nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001764{
Michal Vasko3031aae2016-01-27 16:07:18 +01001765 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001766 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001767
Michal Vasko45e53ae2016-04-07 11:46:03 +02001768 if (!name) {
1769 ERRARG("name");
1770 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001771 }
1772
Michal Vaskoade892d2017-02-22 13:40:35 +01001773 /* BIND LOCK */
1774 pthread_mutex_lock(&server_opts.bind_lock);
1775
1776 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001777 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001778
1779 /* check name uniqueness */
1780 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001781 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001782 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001783 ret = -1;
1784 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001785 }
1786 }
1787
Michal Vasko3031aae2016-01-27 16:07:18 +01001788 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001789 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001790 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001791 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001792 ret = -1;
1793 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001794 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001795 memset(&server_opts.endpts[server_opts.endpt_count - 1], 0, sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001796 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001797 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001798 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
1799 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
1800 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001801
Michal Vaskoe2713da2016-08-22 16:06:40 +02001802 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001803 if (!server_opts.binds) {
1804 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001805 ret = -1;
1806 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001807 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001808
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001809 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001810 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1811
1812 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001813#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001814 case NC_TI_LIBSSH:
1815 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1816 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1817 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001818 ret = -1;
1819 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001820 }
1821 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1822 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1823 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02001824 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001825 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001826#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001827#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001828 case NC_TI_OPENSSL:
1829 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1830 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1831 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001832 ret = -1;
1833 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001834 }
1835 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001836#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001837 case NC_TI_UNIX:
1838 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1839 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1840 ERRMEM;
1841 ret = -1;
1842 goto cleanup;
1843 }
1844 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1845 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1846 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1847 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001848 default:
1849 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001850 ret = -1;
1851 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001852 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001853
Michal Vaskoade892d2017-02-22 13:40:35 +01001854cleanup:
1855 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001856 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001857
Michal Vaskoade892d2017-02-22 13:40:35 +01001858 /* BIND UNLOCK */
1859 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001860
Michal Vaskoade892d2017-02-22 13:40:35 +01001861 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001862}
1863
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001864API int
Michal Vasko59050372016-11-22 14:33:55 +01001865nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001866{
1867 uint32_t i;
1868 int ret = -1;
1869
Michal Vaskoade892d2017-02-22 13:40:35 +01001870 /* BIND LOCK */
1871 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001872
Michal Vaskoade892d2017-02-22 13:40:35 +01001873 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001874 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001875
Michal Vasko59050372016-11-22 14:33:55 +01001876 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001877 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001878 for (i = 0; i < server_opts.endpt_count; ++i) {
1879 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001880 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001881#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001882 case NC_TI_LIBSSH:
1883 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1884 free(server_opts.endpts[i].opts.ssh);
1885 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001886#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001887#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001888 case NC_TI_OPENSSL:
1889 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1890 free(server_opts.endpts[i].opts.tls);
1891 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001892#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001893 case NC_TI_UNIX:
1894 free(server_opts.endpts[i].opts.unixsock);
1895 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001896 default:
1897 ERRINT;
1898 /* won't get here ...*/
1899 break;
1900 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001901 ret = 0;
1902 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001903 free(server_opts.endpts);
1904 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001905
1906 /* remove all binds */
1907 for (i = 0; i < server_opts.endpt_count; ++i) {
1908 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1909 if (server_opts.binds[i].sock > -1) {
1910 close(server_opts.binds[i].sock);
1911 }
1912 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001913 free(server_opts.binds);
1914 server_opts.binds = NULL;
1915
Michal Vasko3031aae2016-01-27 16:07:18 +01001916 server_opts.endpt_count = 0;
1917
Michal Vasko1a38c862016-01-15 15:50:07 +01001918 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001919 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001920 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001921 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001922 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001923 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001924 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001925#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001926 case NC_TI_LIBSSH:
1927 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1928 free(server_opts.endpts[i].opts.ssh);
1929 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001930#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001931#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001932 case NC_TI_OPENSSL:
1933 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1934 free(server_opts.endpts[i].opts.tls);
1935 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001936#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001937 case NC_TI_UNIX:
1938 free(server_opts.endpts[i].opts.unixsock);
1939 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001940 default:
1941 ERRINT;
1942 break;
1943 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001944
Michal Vaskoe2713da2016-08-22 16:06:40 +02001945 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001946 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1947 if (server_opts.binds[i].sock > -1) {
1948 close(server_opts.binds[i].sock);
1949 }
1950
1951 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001952 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001953 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001954 free(server_opts.binds);
1955 server_opts.binds = NULL;
1956 free(server_opts.endpts);
1957 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001958 } else if (i < server_opts.endpt_count) {
1959 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1960 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001961 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001962
1963 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001964 if (name) {
1965 break;
1966 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001967 }
1968 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001969 }
1970
Michal Vaskoade892d2017-02-22 13:40:35 +01001971 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001972 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001973
Michal Vaskoade892d2017-02-22 13:40:35 +01001974 /* BIND UNLOCK */
1975 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001976
1977 return ret;
1978}
1979
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001980API int
1981nc_server_endpt_count(void)
1982{
1983 return server_opts.endpt_count;
1984}
1985
1986int
1987nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
1988{
1989 struct nc_endpt *endpt;
1990 struct nc_bind *bind = NULL;
1991 uint16_t i;
1992 int sock = -1, set_addr, ret = 0;
1993
1994 if (!endpt_name) {
1995 ERRARG("endpt_name");
1996 return -1;
1997 } else if ((!address && !port) || (address && port)) {
1998 ERRARG("address and port");
1999 return -1;
2000 }
2001
2002 if (address) {
2003 set_addr = 1;
2004 } else {
2005 set_addr = 0;
2006 }
2007
2008 /* BIND LOCK */
2009 pthread_mutex_lock(&server_opts.bind_lock);
2010
2011 /* ENDPT LOCK */
2012 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2013 if (!endpt) {
2014 /* BIND UNLOCK */
2015 pthread_mutex_unlock(&server_opts.bind_lock);
2016 return -1;
2017 }
2018
2019 bind = &server_opts.binds[i];
2020
2021 if (set_addr) {
2022 port = bind->port;
2023 } else {
2024 address = bind->address;
2025 }
2026
2027 if (!set_addr && endpt->ti == NC_TI_UNIX) {
2028 ret = -1;
2029 goto cleanup;
2030 }
2031
2032 /* we have all the information we need to create a listening socket */
2033 if (address && (port || endpt->ti == NC_TI_UNIX)) {
2034 /* create new socket, close the old one */
2035 if (endpt->ti == NC_TI_UNIX)
2036 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
2037 else
2038 sock = nc_sock_listen_inet(address, port, &endpt->ka);
2039 if (sock == -1) {
2040 ret = -1;
2041 goto cleanup;
2042 }
2043
2044 if (bind->sock > -1) {
2045 close(bind->sock);
2046 }
2047 bind->sock = sock;
2048 } /* else we are just setting address or port */
2049
2050 if (set_addr) {
2051 lydict_remove(server_opts.ctx, bind->address);
2052 bind->address = lydict_insert(server_opts.ctx, address, 0);
2053 } else {
2054 bind->port = port;
2055 }
2056
2057 if (sock > -1) {
2058#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
2059 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
2060#elif defined(NC_ENABLED_SSH)
2061 VRB("Listening on %s:%u for SSH connections.", address, port);
2062#else
2063 VRB("Listening on %s:%u for TLS connections.", address, port);
2064#endif
2065 }
2066
2067cleanup:
2068 /* ENDPT UNLOCK */
2069 pthread_rwlock_unlock(&server_opts.endpt_lock);
2070
2071 /* BIND UNLOCK */
2072 pthread_mutex_unlock(&server_opts.bind_lock);
2073
2074 return ret;
2075}
2076
2077API int
2078nc_server_endpt_set_address(const char *endpt_name, const char *address)
2079{
2080 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2081}
2082
2083API int
2084nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2085{
2086 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2087}
2088
2089API int
2090nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2091{
2092 struct nc_endpt *endpt;
2093 uint16_t i;
2094 int ret = 0;
2095
2096 if (!endpt_name) {
2097 ERRARG("endpt_name");
2098 return -1;
2099 } else if (mode == 0) {
2100 ERRARG("mode");
2101 return -1;
2102 }
2103
2104 /* ENDPT LOCK */
2105 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2106 if (!endpt)
2107 return -1;
2108
2109 if (endpt->ti != NC_TI_UNIX) {
2110 ret = -1;
2111 goto cleanup;
2112 }
2113
2114 endpt->opts.unixsock->mode = mode;
2115 endpt->opts.unixsock->uid = uid;
2116 endpt->opts.unixsock->gid = gid;
2117
2118cleanup:
2119 /* ENDPT UNLOCK */
2120 pthread_rwlock_unlock(&server_opts.endpt_lock);
2121
2122 return ret;
2123}
2124
2125API int
2126nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2127{
2128 struct nc_endpt *endpt;
2129 int ret = 0;
2130
2131 if (!endpt_name) {
2132 ERRARG("endpt_name");
2133 return -1;
2134 }
2135
2136 /* ENDPT LOCK */
2137 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2138 if (!endpt) {
2139 return -1;
2140 }
2141
2142 endpt->ka.enabled = (enable ? 1 : 0);
2143
2144 /* ENDPT UNLOCK */
2145 pthread_rwlock_unlock(&server_opts.endpt_lock);
2146
2147 return ret;
2148}
2149
2150API int
2151nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2152{
2153 struct nc_endpt *endpt;
2154 int ret = 0;
2155
2156 if (!endpt_name) {
2157 ERRARG("endpt_name");
2158 return -1;
2159 }
2160
2161 /* ENDPT LOCK */
2162 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2163 if (!endpt) {
2164 return -1;
2165 }
2166
2167 if (idle_time > -1) {
2168 endpt->ka.idle_time = idle_time;
2169 }
2170 if (max_probes > -1) {
2171 endpt->ka.max_probes = max_probes;
2172 }
2173 if (probe_interval > -1) {
2174 endpt->ka.probe_interval = probe_interval;
2175 }
2176
2177 /* ENDPT UNLOCK */
2178 pthread_rwlock_unlock(&server_opts.endpt_lock);
2179
2180 return ret;
2181}
2182
Michal Vasko71090fc2016-05-24 16:37:28 +02002183API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002184nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002185{
Michal Vasko71090fc2016-05-24 16:37:28 +02002186 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002187 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002188 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002189 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002190 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002191
Michal Vasko45e53ae2016-04-07 11:46:03 +02002192 if (!server_opts.ctx) {
2193 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002194 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002195 } else if (!session) {
2196 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002197 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002198 }
2199
Michal Vaskoade892d2017-02-22 13:40:35 +01002200 /* BIND LOCK */
2201 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002202
2203 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02002204 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002205 /* BIND UNLOCK */
2206 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002207 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002208 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002209
Michal Vaskoe2713da2016-08-22 16:06:40 +02002210 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002211 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002212 /* BIND UNLOCK */
2213 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002214 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002215 if (!ret) {
2216 return NC_MSG_WOULDBLOCK;
2217 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002218 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002219 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002220
2221 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2222 /* ENDPT READ LOCK */
2223 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2224
2225 /* BIND UNLOCK */
2226 pthread_mutex_unlock(&server_opts.bind_lock);
2227
Michal Vaskob48aa812016-01-18 14:13:09 +01002228 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002229
Michal Vasko131120a2018-05-29 15:44:02 +02002230 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002231 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002232 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002233 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002234 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002235 msgtype = NC_MSG_ERROR;
2236 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002237 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002238 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002239 (*session)->ctx = server_opts.ctx;
2240 (*session)->flags = NC_SESSION_SHAREDCTX;
2241 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
2242 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002243
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002244 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002245#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002246 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2247 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002248 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002249 if (ret < 0) {
2250 msgtype = NC_MSG_ERROR;
2251 goto cleanup;
2252 } else if (!ret) {
2253 msgtype = NC_MSG_WOULDBLOCK;
2254 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002255 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002256 } else
2257#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002258#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002259 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2260 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002261 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002262 if (ret < 0) {
2263 msgtype = NC_MSG_ERROR;
2264 goto cleanup;
2265 } else if (!ret) {
2266 msgtype = NC_MSG_WOULDBLOCK;
2267 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002268 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002269 } else
2270#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002271 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2272 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2273 ret = nc_accept_unix(*session, sock);
2274 if (ret < 0) {
2275 msgtype = NC_MSG_ERROR;
2276 goto cleanup;
2277 }
2278 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002279 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002280 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002281 msgtype = NC_MSG_ERROR;
2282 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002283 }
2284
Michal Vasko2cc4c682016-03-01 09:16:48 +01002285 (*session)->data = NULL;
2286
Michal Vaskoade892d2017-02-22 13:40:35 +01002287 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002288 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002289
Michal Vaskob48aa812016-01-18 14:13:09 +01002290 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002291 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002292
Michal Vasko9e036d52016-01-08 10:49:26 +01002293 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002294 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002295 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002296 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002297 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002298 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002299 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002300
2301 nc_gettimespec_mono(&ts_cur);
2302 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2303 nc_gettimespec_real(&ts_cur);
2304 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002305 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002306
Michal Vasko71090fc2016-05-24 16:37:28 +02002307 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002308
Michal Vasko71090fc2016-05-24 16:37:28 +02002309cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002310 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002311 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002312
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002313 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002314 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002315 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002316}
2317
Michal Vaskoadf30f02019-06-24 09:34:47 +02002318/* client is expected to be locked */
2319static int
2320_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 +02002321{
2322 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002323 int ret = -1;
2324
2325 if (!endpt_name) {
2326 /* remove all endpoints */
2327 for (i = 0; i < client->ch_endpt_count; ++i) {
2328 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2329 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2330 if (client->ch_endpts[i].sock_pending != -1) {
2331 close(client->ch_endpts[i].sock_pending);
2332 }
2333 switch (client->ch_endpts[i].ti) {
2334#ifdef NC_ENABLED_SSH
2335 case NC_TI_LIBSSH:
2336 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2337 free(client->ch_endpts[i].opts.ssh);
2338 break;
2339#endif
2340#ifdef NC_ENABLED_TLS
2341 case NC_TI_OPENSSL:
2342 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2343 free(client->ch_endpts[i].opts.tls);
2344 break;
2345#endif
2346 default:
2347 ERRINT;
2348 /* won't get here ...*/
2349 break;
2350 }
2351 }
2352 free(client->ch_endpts);
2353 client->ch_endpts = NULL;
2354 client->ch_endpt_count = 0;
2355
2356 ret = 0;
2357 } else {
2358 for (i = 0; i < client->ch_endpt_count; ++i) {
2359 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
2360 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2361 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2362 if (client->ch_endpts[i].sock_pending != -1) {
2363 close(client->ch_endpts[i].sock_pending);
2364 }
2365 switch (client->ch_endpts[i].ti) {
2366#ifdef NC_ENABLED_SSH
2367 case NC_TI_LIBSSH:
2368 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2369 free(client->ch_endpts[i].opts.ssh);
2370 break;
2371#endif
2372#ifdef NC_ENABLED_TLS
2373 case NC_TI_OPENSSL:
2374 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2375 free(client->ch_endpts[i].opts.tls);
2376 break;
2377#endif
2378 default:
2379 ERRINT;
2380 /* won't get here ...*/
2381 break;
2382 }
2383
2384 /* move last endpoint to the empty space */
2385 --client->ch_endpt_count;
2386 if (i < client->ch_endpt_count) {
2387 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2388 } else if (!server_opts.ch_client_count) {
2389 free(server_opts.ch_clients);
2390 server_opts.ch_clients = NULL;
2391 }
2392
2393 ret = 0;
2394 break;
2395 }
2396 }
2397 }
2398
2399 return ret;
2400}
2401
2402API int
2403nc_server_ch_add_client(const char *name)
2404{
2405 uint16_t i;
2406 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002407
2408 if (!name) {
2409 ERRARG("name");
2410 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002411 }
2412
2413 /* WRITE LOCK */
2414 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2415
2416 /* check name uniqueness */
2417 for (i = 0; i < server_opts.ch_client_count; ++i) {
2418 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2419 ERR("Call Home client \"%s\" already exists.", name);
2420 /* WRITE UNLOCK */
2421 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2422 return -1;
2423 }
2424 }
2425
2426 ++server_opts.ch_client_count;
2427 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2428 if (!server_opts.ch_clients) {
2429 ERRMEM;
2430 /* WRITE UNLOCK */
2431 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2432 return -1;
2433 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002434 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002435
Michal Vaskoadf30f02019-06-24 09:34:47 +02002436 client->name = lydict_insert(server_opts.ctx, name, 0);
2437 client->id = ATOMIC_INC(&server_opts.new_client_id);
2438 client->ch_endpts = NULL;
2439 client->ch_endpt_count = 0;
2440 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002441
Michal Vasko2e6defd2016-10-07 15:48:15 +02002442 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002443 client->start_with = NC_CH_FIRST_LISTED;
2444 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002445
Michal Vaskoadf30f02019-06-24 09:34:47 +02002446 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002447
2448 /* WRITE UNLOCK */
2449 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2450
2451 return 0;
2452}
2453
2454API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002455nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002456{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002457 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002458 int ret = -1;
2459
2460 /* WRITE LOCK */
2461 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2462
Michal Vaskoadf30f02019-06-24 09:34:47 +02002463 if (!name) {
2464 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002465 for (i = 0; i < server_opts.ch_client_count; ++i) {
2466 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2467
2468 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002469 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002470
2471 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002472 ret = 0;
2473 }
2474 free(server_opts.ch_clients);
2475 server_opts.ch_clients = NULL;
2476
2477 server_opts.ch_client_count = 0;
2478
2479 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002480 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002481 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002482 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002483 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2484
Michal Vasko2e6defd2016-10-07 15:48:15 +02002485 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002486 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002487
2488 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2489
2490 /* move last client and endpoint(s) to the empty space */
2491 --server_opts.ch_client_count;
2492 if (i < server_opts.ch_client_count) {
2493 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002494 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002495 } else if (!server_opts.ch_client_count) {
2496 free(server_opts.ch_clients);
2497 server_opts.ch_clients = NULL;
2498 }
2499
2500 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002501 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002502 }
2503 }
2504 }
2505
2506 /* WRITE UNLOCK */
2507 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2508
2509 return ret;
2510}
2511
2512API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002513nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002514{
2515 uint16_t i;
2516 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002517 struct nc_ch_endpt *endpt;
2518 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002519
2520 if (!client_name) {
2521 ERRARG("client_name");
2522 return -1;
2523 } else if (!endpt_name) {
2524 ERRARG("endpt_name");
2525 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002526 } else if (!ti) {
2527 ERRARG("ti");
2528 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002529 }
2530
2531 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002532 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002533 if (!client) {
2534 return -1;
2535 }
2536
2537 for (i = 0; i < client->ch_endpt_count; ++i) {
2538 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2539 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002540 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002541 }
2542 }
2543
2544 ++client->ch_endpt_count;
2545 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2546 if (!client->ch_endpts) {
2547 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002548 goto cleanup;
2549 }
2550 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2551
2552 memset(endpt, 0, sizeof *client->ch_endpts);
2553 endpt->name = lydict_insert(server_opts.ctx, endpt_name, 0);
2554 endpt->ti = ti;
2555 endpt->sock_pending = -1;
2556 endpt->ka.idle_time = 1;
2557 endpt->ka.max_probes = 10;
2558 endpt->ka.probe_interval = 5;
2559
2560 switch (ti) {
2561#ifdef NC_ENABLED_SSH
2562 case NC_TI_LIBSSH:
2563 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2564 if (!endpt->opts.ssh) {
2565 ERRMEM;
2566 goto cleanup;
2567 }
2568 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2569 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002570 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002571 break;
2572#endif
2573#ifdef NC_ENABLED_TLS
2574 case NC_TI_OPENSSL:
2575 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2576 if (!endpt->opts.tls) {
2577 ERRMEM;
2578 goto cleanup;
2579 }
2580 break;
2581#endif
2582 default:
2583 ERRINT;
2584 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002585 }
2586
Michal Vaskoadf30f02019-06-24 09:34:47 +02002587 /* success */
2588 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002589
Michal Vaskoadf30f02019-06-24 09:34:47 +02002590cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002591 /* UNLOCK */
2592 nc_server_ch_client_unlock(client);
2593
Michal Vaskoadf30f02019-06-24 09:34:47 +02002594 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002595}
2596
2597API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002598nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002599{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002600 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002601 struct nc_ch_client *client;
2602
2603 if (!client_name) {
2604 ERRARG("client_name");
2605 return -1;
2606 }
2607
2608 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002609 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002610 if (!client) {
2611 return -1;
2612 }
2613
Michal Vaskoadf30f02019-06-24 09:34:47 +02002614 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002615
2616 /* UNLOCK */
2617 nc_server_ch_client_unlock(client);
2618
2619 return ret;
2620}
2621
2622API int
2623nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2624{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002625 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002626 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002627
2628 if (!client_name) {
2629 ERRARG("client_name");
2630 return -1;
2631 } else if (!endpt_name) {
2632 ERRARG("endpt_name");
2633 return -1;
2634 } else if (!address) {
2635 ERRARG("address");
2636 return -1;
2637 }
2638
2639 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002640 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2641 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002642 return -1;
2643 }
2644
Michal Vaskoadf30f02019-06-24 09:34:47 +02002645 lydict_remove(server_opts.ctx, endpt->address);
2646 endpt->address = lydict_insert(server_opts.ctx, address, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002647
2648 /* UNLOCK */
2649 nc_server_ch_client_unlock(client);
2650
Michal Vaskoadf30f02019-06-24 09:34:47 +02002651 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002652}
2653
2654API int
2655nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2656{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002657 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002658 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002659
2660 if (!client_name) {
2661 ERRARG("client_name");
2662 return -1;
2663 } else if (!endpt_name) {
2664 ERRARG("endpt_name");
2665 return -1;
2666 } else if (!port) {
2667 ERRARG("port");
2668 return -1;
2669 }
2670
2671 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002672 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2673 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002674 return -1;
2675 }
2676
Michal Vaskoadf30f02019-06-24 09:34:47 +02002677 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002678
2679 /* UNLOCK */
2680 nc_server_ch_client_unlock(client);
2681
Michal Vaskoadf30f02019-06-24 09:34:47 +02002682 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002683}
2684
2685API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002686nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
2687{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002688 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002689 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002690
2691 if (!client_name) {
2692 ERRARG("client_name");
2693 return -1;
2694 } else if (!endpt_name) {
2695 ERRARG("endpt_name");
2696 return -1;
2697 }
2698
2699 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002700 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2701 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002702 return -1;
2703 }
2704
Michal Vaskoadf30f02019-06-24 09:34:47 +02002705 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002706
2707 /* UNLOCK */
2708 nc_server_ch_client_unlock(client);
2709
Michal Vasko9af829a2019-09-12 13:50:00 +02002710 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002711}
2712
2713API int
2714nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
2715 int probe_interval)
2716{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002717 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002718 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002719
2720 if (!client_name) {
2721 ERRARG("client_name");
2722 return -1;
2723 } else if (!endpt_name) {
2724 ERRARG("endpt_name");
2725 return -1;
2726 }
2727
2728 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002729 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2730 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002731 return -1;
2732 }
2733
Michal Vaskoadf30f02019-06-24 09:34:47 +02002734 if (idle_time > -1) {
2735 endpt->ka.idle_time = idle_time;
2736 }
2737 if (max_probes > -1) {
2738 endpt->ka.max_probes = max_probes;
2739 }
2740 if (probe_interval > -1) {
2741 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002742 }
2743
2744 /* UNLOCK */
2745 nc_server_ch_client_unlock(client);
2746
Michal Vasko9af829a2019-09-12 13:50:00 +02002747 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002748}
2749
2750API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002751nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2752{
2753 struct nc_ch_client *client;
2754
2755 if (!client_name) {
2756 ERRARG("client_name");
2757 return -1;
2758 } else if (!conn_type) {
2759 ERRARG("conn_type");
2760 return -1;
2761 }
2762
2763 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002764 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002765 if (!client) {
2766 return -1;
2767 }
2768
2769 if (client->conn_type != conn_type) {
2770 client->conn_type = conn_type;
2771
2772 /* set default options */
2773 switch (conn_type) {
2774 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002775 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002776 break;
2777 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002778 client->conn.period.period = 60;
2779 client->conn.period.anchor_time = 0;
2780 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002781 break;
2782 default:
2783 ERRINT;
2784 break;
2785 }
2786 }
2787
2788 /* UNLOCK */
2789 nc_server_ch_client_unlock(client);
2790
2791 return 0;
2792}
2793
2794API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002795nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
2796{
2797 struct nc_ch_client *client;
2798
2799 if (!client_name) {
2800 ERRARG("client_name");
2801 return -1;
2802 } else if (!period) {
2803 ERRARG("period");
2804 return -1;
2805 }
2806
2807 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002808 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002809 if (!client) {
2810 return -1;
2811 }
2812
2813 if (client->conn_type != NC_CH_PERIOD) {
2814 ERR("Call Home client \"%s\" is not of periodic connection type.");
2815 /* UNLOCK */
2816 nc_server_ch_client_unlock(client);
2817 return -1;
2818 }
2819
2820 client->conn.period.period = period;
2821
2822 /* UNLOCK */
2823 nc_server_ch_client_unlock(client);
2824
2825 return 0;
2826}
2827
2828API int
2829nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002830{
2831 struct nc_ch_client *client;
2832
2833 if (!client_name) {
2834 ERRARG("client_name");
2835 return -1;
2836 }
2837
2838 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002839 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002840 if (!client) {
2841 return -1;
2842 }
2843
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002844 if (client->conn_type != NC_CH_PERIOD) {
2845 ERR("Call Home client \"%s\" is not of periodic connection type.");
Michal Vasko2e6defd2016-10-07 15:48:15 +02002846 /* UNLOCK */
2847 nc_server_ch_client_unlock(client);
2848 return -1;
2849 }
2850
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002851 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002852
2853 /* UNLOCK */
2854 nc_server_ch_client_unlock(client);
2855
2856 return 0;
2857}
2858
2859API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002860nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002861{
2862 struct nc_ch_client *client;
2863
2864 if (!client_name) {
2865 ERRARG("client_name");
2866 return -1;
2867 }
2868
2869 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002870 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002871 if (!client) {
2872 return -1;
2873 }
2874
2875 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002876 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002877 /* UNLOCK */
2878 nc_server_ch_client_unlock(client);
2879 return -1;
2880 }
2881
2882 client->conn.period.idle_timeout = idle_timeout;
2883
2884 /* UNLOCK */
2885 nc_server_ch_client_unlock(client);
2886
2887 return 0;
2888}
2889
2890API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002891nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2892{
2893 struct nc_ch_client *client;
2894
2895 if (!client_name) {
2896 ERRARG("client_name");
2897 return -1;
2898 }
2899
2900 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002901 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002902 if (!client) {
2903 return -1;
2904 }
2905
2906 client->start_with = start_with;
2907
2908 /* UNLOCK */
2909 nc_server_ch_client_unlock(client);
2910
2911 return 0;
2912}
2913
2914API int
2915nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2916{
2917 struct nc_ch_client *client;
2918
2919 if (!client_name) {
2920 ERRARG("client_name");
2921 return -1;
2922 } else if (!max_attempts) {
2923 ERRARG("max_attempts");
2924 return -1;
2925 }
2926
2927 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002928 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002929 if (!client) {
2930 return -1;
2931 }
2932
2933 client->max_attempts = max_attempts;
2934
2935 /* UNLOCK */
2936 nc_server_ch_client_unlock(client);
2937
2938 return 0;
2939}
2940
2941/* client lock is expected to be held */
2942static NC_MSG_TYPE
Michal Vaskoadf30f02019-06-24 09:34:47 +02002943nc_connect_ch_endpt(struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01002944{
Michal Vasko71090fc2016-05-24 16:37:28 +02002945 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002946 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002947 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01002948 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01002949
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002950 sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->ka, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002951 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002952 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002953 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00002954 /* no need to store the socket as pending any longer */
2955 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01002956
Michal Vasko131120a2018-05-29 15:44:02 +02002957 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002958 if (!(*session)) {
2959 ERRMEM;
2960 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01002961 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002962 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002963 }
2964 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01002965 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002966 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko66032bc2019-01-22 15:03:12 +01002967 (*session)->host = lydict_insert_zc(server_opts.ctx, ip_host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002968 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002969
Michal Vaskob05053d2016-01-22 16:12:06 +01002970 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002971#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02002972 if (endpt->ti == NC_TI_LIBSSH) {
2973 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002974 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002975 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002976
Michal Vasko71090fc2016-05-24 16:37:28 +02002977 if (ret < 0) {
2978 msgtype = NC_MSG_ERROR;
2979 goto fail;
2980 } else if (!ret) {
2981 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002982 goto fail;
2983 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002984 } else
2985#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002986#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02002987 if (endpt->ti == NC_TI_OPENSSL) {
2988 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002989 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002990 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002991
Michal Vasko71090fc2016-05-24 16:37:28 +02002992 if (ret < 0) {
2993 msgtype = NC_MSG_ERROR;
2994 goto fail;
2995 } else if (!ret) {
2996 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002997 goto fail;
2998 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002999 } else
3000#endif
3001 {
Michal Vaskob05053d2016-01-22 16:12:06 +01003002 ERRINT;
3003 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003004 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003005 goto fail;
3006 }
3007
3008 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01003009 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003010
3011 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003012 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003013 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003014 goto fail;
3015 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003016
3017 nc_gettimespec_mono(&ts_cur);
3018 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
3019 nc_gettimespec_real(&ts_cur);
3020 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003021 (*session)->status = NC_STATUS_RUNNING;
3022
Michal Vasko71090fc2016-05-24 16:37:28 +02003023 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003024
3025fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003026 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003027 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02003028 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003029}
3030
Michal Vasko2e6defd2016-10-07 15:48:15 +02003031struct nc_ch_client_thread_arg {
3032 char *client_name;
3033 void (*session_clb)(const char *client_name, struct nc_session *new_session);
3034};
3035
3036static struct nc_ch_client *
3037nc_server_ch_client_with_endpt_lock(const char *name)
3038{
3039 struct nc_ch_client *client;
3040
3041 while (1) {
3042 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003043 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003044 if (!client) {
3045 return NULL;
3046 }
3047 if (client->ch_endpt_count) {
3048 return client;
3049 }
3050 /* no endpoints defined yet */
3051
3052 /* UNLOCK */
3053 nc_server_ch_client_unlock(client);
3054
3055 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3056 }
3057
3058 return NULL;
3059}
3060
3061static int
3062nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3063{
Michal Vasko3f05a092018-03-13 10:39:49 +01003064 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003065 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003066 struct timespec ts;
3067 struct nc_ch_client *client;
3068
3069 /* session created, initialize condition */
Michal Vasko27377422018-03-15 08:59:35 +01003070 session->opts.server.ch_lock = calloc(1, sizeof *session->opts.server.ch_lock);
3071 session->opts.server.ch_cond = calloc(1, sizeof *session->opts.server.ch_cond);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003072 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
3073 ERRMEM;
3074 nc_session_free(session, NULL);
3075 return -1;
3076 }
3077 pthread_mutex_init(session->opts.server.ch_lock, NULL);
3078 pthread_cond_init(session->opts.server.ch_cond, NULL);
3079
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003080 session->flags |= NC_SESSION_CALLHOME;
3081
Michal Vasko2e6defd2016-10-07 15:48:15 +02003082 /* CH LOCK */
3083 pthread_mutex_lock(session->opts.server.ch_lock);
3084
3085 /* give the session to the user */
3086 data->session_clb(data->client_name, session);
3087
3088 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003089 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003090 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003091
Michal Vasko3f05a092018-03-13 10:39:49 +01003092 r = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
3093 if (!r) {
3094 /* we were woken up, something probably happened */
3095 if (session->status != NC_STATUS_RUNNING) {
3096 break;
3097 }
3098 } else if (r != ETIMEDOUT) {
3099 ERR("Pthread condition timedwait failed (%s).", strerror(r));
3100 ret = -1;
3101 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003102 }
3103
Michal Vasko2e6defd2016-10-07 15:48:15 +02003104 /* check whether the client was not removed */
3105 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003106 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003107 if (!client) {
3108 /* client was removed, finish thread */
3109 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003110 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003111 ret = 1;
3112 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003113 }
3114
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003115 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003116 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003117 } else {
3118 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003119 }
3120
Michal Vasko9fb42272017-10-05 13:50:05 +02003121 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003122 if (!session->opts.server.ntf_status && idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003123 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
3124 session->status = NC_STATUS_INVALID;
3125 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3126 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003127
3128 /* UNLOCK */
3129 nc_server_ch_client_unlock(client);
3130
3131 } while (session->status == NC_STATUS_RUNNING);
3132
Michal Vasko27377422018-03-15 08:59:35 +01003133 /* CH UNLOCK */
3134 pthread_mutex_unlock(session->opts.server.ch_lock);
3135
Michal Vasko3f05a092018-03-13 10:39:49 +01003136 if (session->status == NC_STATUS_CLOSING) {
3137 /* signal to nc_session_free() that we registered session being freed, otherwise it matters not */
3138 session->flags &= ~NC_SESSION_CALLHOME;
3139 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003140
Michal Vasko3f05a092018-03-13 10:39:49 +01003141 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003142}
3143
3144static void *
3145nc_ch_client_thread(void *arg)
3146{
3147 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3148 NC_MSG_TYPE msgtype;
3149 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003150 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003151 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003152 struct nc_ch_endpt *cur_endpt;
3153 struct nc_session *session;
3154 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003155 uint32_t client_id;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003156 time_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003157
3158 /* LOCK */
3159 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3160 if (!client) {
3161 goto cleanup;
3162 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003163 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003164
3165 cur_endpt = &client->ch_endpts[0];
3166 cur_endpt_name = strdup(cur_endpt->name);
3167
Michal Vasko29af44b2016-10-13 10:59:55 +02003168 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003169 while (1) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02003170 msgtype = nc_connect_ch_endpt(cur_endpt, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003171
3172 if (msgtype == NC_MSG_HELLO) {
3173 /* UNLOCK */
3174 nc_server_ch_client_unlock(client);
3175
Michal Vasko29af44b2016-10-13 10:59:55 +02003176 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003177 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3178 goto cleanup;
3179 }
Michal Vasko29af44b2016-10-13 10:59:55 +02003180 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003181
3182 /* LOCK */
3183 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3184 if (!client) {
3185 goto cleanup;
3186 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003187 if (client->id != client_id) {
3188 nc_server_ch_client_unlock(client);
3189 goto cleanup;
3190 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003191
3192 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003193 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003194 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003195 /* UNLOCK */
3196 nc_server_ch_client_unlock(client);
3197
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003198 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
3199 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
3200 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003201
3202 /* LOCK */
3203 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3204 if (!client) {
3205 goto cleanup;
3206 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003207 if (client->id != client_id) {
3208 nc_server_ch_client_unlock(client);
3209 goto cleanup;
3210 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003211 }
3212
3213 /* set next endpoint to try */
3214 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003215 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003216 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003217 /* we keep the current one but due to unlock/lock we have to find it again */
3218 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3219 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3220 break;
3221 }
3222 }
3223 if (next_endpt_index >= client->ch_endpt_count) {
3224 /* endpoint was removed, start with the first one */
3225 next_endpt_index = 0;
3226 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003227 } else {
3228 /* just get a random index */
3229 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003230 }
3231
Michal Vasko2e6defd2016-10-07 15:48:15 +02003232 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003233 /* UNLOCK */
3234 nc_server_ch_client_unlock(client);
3235
Michal Vasko2e6defd2016-10-07 15:48:15 +02003236 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003237 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3238
Michal Vasko6bb116b2016-10-26 13:53:46 +02003239 /* LOCK */
3240 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3241 if (!client) {
3242 goto cleanup;
3243 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003244 if (client->id != client_id) {
3245 nc_server_ch_client_unlock(client);
3246 goto cleanup;
3247 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003248
Michal Vasko2e6defd2016-10-07 15:48:15 +02003249 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003250
3251 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003252 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3253 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003254 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003255 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003256 }
3257
Peter Feiged05f2252018-09-03 08:09:47 +00003258 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003259 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003260 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003261 cur_attempts = 0;
3262 } else if (cur_attempts == client->max_attempts) {
3263 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003264 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003265 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003266 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003267 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003268 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003269 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003270 }
3271
3272 cur_attempts = 0;
3273 } /* else we keep the current one */
3274 }
Peter Feiged05f2252018-09-03 08:09:47 +00003275
3276 cur_endpt = &client->ch_endpts[next_endpt_index];
3277 free(cur_endpt_name);
3278 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003279 }
3280
3281cleanup:
3282 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003283 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003284 free(data->client_name);
3285 free(data);
3286 return NULL;
3287}
3288
3289API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02003290nc_connect_ch_client_dispatch(const char *client_name, void (*session_clb)(const char *client_name,
3291 struct nc_session *new_session))
Michal Vasko3f05a092018-03-13 10:39:49 +01003292{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003293 int ret;
3294 pthread_t tid;
3295 struct nc_ch_client_thread_arg *arg;
3296
3297 if (!client_name) {
3298 ERRARG("client_name");
3299 return -1;
3300 } else if (!session_clb) {
3301 ERRARG("session_clb");
3302 return -1;
3303 }
3304
3305 arg = malloc(sizeof *arg);
3306 if (!arg) {
3307 ERRMEM;
3308 return -1;
3309 }
3310 arg->client_name = strdup(client_name);
3311 if (!arg->client_name) {
3312 ERRMEM;
3313 free(arg);
3314 return -1;
3315 }
3316 arg->session_clb = session_clb;
3317
3318 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3319 if (ret) {
3320 ERR("Creating a new thread failed (%s).", strerror(ret));
3321 free(arg->client_name);
3322 free(arg);
3323 return -1;
3324 }
3325 /* the thread now manages arg */
3326
3327 pthread_detach(tid);
3328
3329 return 0;
3330}
3331
Radek Krejci53691be2016-02-22 13:58:37 +01003332#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003333
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003334API time_t
3335nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003336{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003337 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003338 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003339 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003340 }
3341
Michal Vasko2e6defd2016-10-07 15:48:15 +02003342 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003343}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003344
3345API void
3346nc_session_set_notif_status(struct nc_session *session, int notif_status)
3347{
3348 if (!session || (session->side != NC_SERVER)) {
3349 ERRARG("session");
3350 return;
3351 }
3352
3353 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3354}
3355
3356API int
3357nc_session_get_notif_status(const struct nc_session *session)
3358{
3359 if (!session || (session->side != NC_SERVER)) {
3360 ERRARG("session");
3361 return 0;
3362 }
3363
3364 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003365}
Michal Vasko8f430592019-02-26 08:32:54 +01003366
3367API int
3368nc_session_is_callhome(const struct nc_session *session)
3369{
3370 if (!session || (session->side != NC_SERVER)) {
3371 ERRARG("session");
3372 return 0;
3373 }
3374
3375 if (session->flags & NC_SESSION_CALLHOME) {
3376 return 1;
3377 }
3378
3379 return 0;
3380}