blob: f2a68385b36f270e27ca1e6c1e7b8800e16f7fe9 [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) {
591 if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
592 if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
593 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
594 }
595 if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
596 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
597 }
598 } else {
599 ERR("%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
600 }
601 pthread_rwlockattr_destroy(&attr);
602 } else {
603 ERR("%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
604 }
Michal Vasko086311b2016-01-08 09:53:11 +0100605 return 0;
606}
607
Michal Vaskob48aa812016-01-18 14:13:09 +0100608API void
609nc_server_destroy(void)
610{
Radek Krejci658782b2016-12-04 22:04:55 +0100611 unsigned int i;
612
613 for (i = 0; i < server_opts.capabilities_count; i++) {
614 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
615 }
616 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200617 server_opts.capabilities = NULL;
618 server_opts.capabilities_count = 0;
619
Radek Krejci53691be2016-02-22 13:58:37 +0100620#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100621 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200622 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100623#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100624#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100625 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
626 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
627 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200628 server_opts.passwd_auth_data = NULL;
629 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100630
Michal Vasko17dfda92016-12-01 14:06:16 +0100631 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100632
633 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
634 server_opts.hostkey_data_free(server_opts.hostkey_data);
635 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200636 server_opts.hostkey_data = NULL;
637 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100638#endif
639#ifdef NC_ENABLED_TLS
640 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
641 server_opts.server_cert_data_free(server_opts.server_cert_data);
642 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200643 server_opts.server_cert_data = NULL;
644 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100645 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
646 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
647 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200648 server_opts.trusted_cert_list_data = NULL;
649 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100650#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100651 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100652}
653
Michal Vasko086311b2016-01-08 09:53:11 +0100654API int
655nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
656{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200657 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
658 ERRARG("basic_mode");
659 return -1;
660 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
661 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100662 return -1;
663 }
664
665 server_opts.wd_basic_mode = basic_mode;
666 server_opts.wd_also_supported = also_supported;
667 return 0;
668}
669
Michal Vasko1a38c862016-01-15 15:50:07 +0100670API void
Michal Vasko55f03972016-04-13 08:56:01 +0200671nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
672{
673 if (!basic_mode && !also_supported) {
674 ERRARG("basic_mode and also_supported");
675 return;
676 }
677
678 if (basic_mode) {
679 *basic_mode = server_opts.wd_basic_mode;
680 }
681 if (also_supported) {
682 *also_supported = server_opts.wd_also_supported;
683 }
684}
685
Michal Vasko55f03972016-04-13 08:56:01 +0200686API int
Radek Krejci658782b2016-12-04 22:04:55 +0100687nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200688{
Radek Krejci658782b2016-12-04 22:04:55 +0100689 const char **new;
690
691 if (!value || !value[0]) {
692 ERRARG("value must not be empty");
693 return EXIT_FAILURE;
694 }
695
696 server_opts.capabilities_count++;
697 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
698 if (!new) {
699 ERRMEM;
700 return EXIT_FAILURE;
701 }
702 server_opts.capabilities = new;
703 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
704
705 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200706}
707
Michal Vasko1a38c862016-01-15 15:50:07 +0100708API void
Michal Vasko086311b2016-01-08 09:53:11 +0100709nc_server_set_hello_timeout(uint16_t hello_timeout)
710{
Michal Vasko086311b2016-01-08 09:53:11 +0100711 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100712}
713
Michal Vasko55f03972016-04-13 08:56:01 +0200714API uint16_t
715nc_server_get_hello_timeout(void)
716{
717 return server_opts.hello_timeout;
718}
719
Michal Vasko1a38c862016-01-15 15:50:07 +0100720API void
Michal Vasko086311b2016-01-08 09:53:11 +0100721nc_server_set_idle_timeout(uint16_t idle_timeout)
722{
Michal Vasko086311b2016-01-08 09:53:11 +0100723 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100724}
725
Michal Vasko55f03972016-04-13 08:56:01 +0200726API uint16_t
727nc_server_get_idle_timeout(void)
728{
729 return server_opts.idle_timeout;
730}
731
Michal Vasko71090fc2016-05-24 16:37:28 +0200732API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100733nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100734{
Michal Vasko71090fc2016-05-24 16:37:28 +0200735 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200736 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200737
Michal Vasko45e53ae2016-04-07 11:46:03 +0200738 if (!server_opts.ctx) {
739 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200740 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200741 } else if (fdin < 0) {
742 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200743 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200744 } else if (fdout < 0) {
745 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200746 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200747 } else if (!username) {
748 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200749 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200750 } else if (!session) {
751 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200752 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100753 }
754
755 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200756 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100757 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100758 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200759 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100760 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100761 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100762
Michal Vasko086311b2016-01-08 09:53:11 +0100763 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100764 (*session)->ti_type = NC_TI_FD;
765 (*session)->ti.fd.in = fdin;
766 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100767
768 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100769 (*session)->flags = NC_SESSION_SHAREDCTX;
770 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100771
Michal Vaskob48aa812016-01-18 14:13:09 +0100772 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +0100773 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100774
Michal Vasko086311b2016-01-08 09:53:11 +0100775 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200776 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200777 if (msgtype != NC_MSG_HELLO) {
778 nc_session_free(*session, NULL);
779 *session = NULL;
780 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100781 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200782
783 nc_gettimespec_mono(&ts_cur);
784 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
785 nc_gettimespec_real(&ts_cur);
786 (*session)->opts.server.session_start = ts_cur.tv_sec;
787
Michal Vasko1a38c862016-01-15 15:50:07 +0100788 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100789
Michal Vasko71090fc2016-05-24 16:37:28 +0200790 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100791}
Michal Vasko9e036d52016-01-08 10:49:26 +0100792
Michal Vaskob30b99c2016-07-26 11:35:43 +0200793static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100794nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
795{
796 uint8_t q_last;
797
798 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
799 ERRINT;
800 return;
801 }
802
803 /* get a unique queue value (by adding 1 to the last added value, if any) */
804 if (ps->queue_len) {
805 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
806 *id = ps->queue[q_last] + 1;
807 } else {
808 *id = 0;
809 }
810
811 /* add the id into the queue */
812 ++ps->queue_len;
813 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
814 ps->queue[q_last] = *id;
815}
816
817static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200818nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
819{
Michal Vasko74c345f2018-02-07 10:37:11 +0100820 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200821
822 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100823 /* get the actual queue idx */
824 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200825
826 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100827 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200828 /* another equal value, simply cannot be */
829 ERRINT;
830 }
Michal Vaskod8340032018-02-12 14:41:00 +0100831 if (found == 2) {
832 /* move the following values */
833 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
834 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100835 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200836 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100837 if (i == 0) {
838 found = 1;
839 } else {
840 /* this is not okay, our id is in the middle of the queue */
841 found = 2;
842 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200843 }
844 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200845 if (!found) {
846 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100847 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200848 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100849
Michal Vasko103fe632018-02-12 16:37:45 +0100850 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100851 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100852 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100853 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
854 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200855}
856
Michal Vaskof04a52a2016-04-07 10:52:10 +0200857int
Michal Vasko26043172016-07-26 14:08:59 +0200858nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200859{
860 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200861 struct timespec ts;
862
Michal Vasko77a6abe2017-10-05 10:02:20 +0200863 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100864 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200865
866 /* LOCK */
867 ret = pthread_mutex_timedlock(&ps->lock, &ts);
868 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200869 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200870 return -1;
871 }
872
Michal Vasko74c345f2018-02-07 10:37:11 +0100873 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +0100874 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100875 ERR("%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +0100876 pthread_mutex_unlock(&ps->lock);
877 return -1;
878 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100879
880 /* add ourselves into the queue */
881 nc_ps_queue_add_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200882
883 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200884 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200885 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +0100886 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200887
888 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
889 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +0530890 /**
891 * This may happen when another thread releases the lock and broadcasts the condition
892 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
893 * but when actually this thread was ready for condition.
894 */
preetbhansali629dfc42018-12-17 16:04:40 +0530895 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +0530896 break;
897 }
Michal Vasko66032bc2019-01-22 15:03:12 +0100898
Michal Vasko26043172016-07-26 14:08:59 +0200899 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200900 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200901 nc_ps_queue_remove_id(ps, *id);
902 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200903 return -1;
904 }
905 }
906
Michal Vaskobe86fe32016-04-07 10:43:03 +0200907 /* UNLOCK */
908 pthread_mutex_unlock(&ps->lock);
909
910 return 0;
911}
912
Michal Vaskof04a52a2016-04-07 10:52:10 +0200913int
Michal Vasko26043172016-07-26 14:08:59 +0200914nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200915{
916 int ret;
917 struct timespec ts;
918
Michal Vasko77a6abe2017-10-05 10:02:20 +0200919 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100920 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200921
922 /* LOCK */
923 ret = pthread_mutex_timedlock(&ps->lock, &ts);
924 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200925 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200926 ret = -1;
927 }
928
Michal Vaskob30b99c2016-07-26 11:35:43 +0200929 /* we must be the first, it was our turn after all, right? */
930 if (ps->queue[ps->queue_begin] != id) {
931 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200932 /* UNLOCK */
933 if (!ret) {
934 pthread_mutex_unlock(&ps->lock);
935 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200936 return -1;
937 }
938
Michal Vaskobe86fe32016-04-07 10:43:03 +0200939 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200940 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200941
942 /* broadcast to all other threads that the queue moved */
943 pthread_cond_broadcast(&ps->cond);
944
Michal Vaskobe86fe32016-04-07 10:43:03 +0200945 /* UNLOCK */
946 if (!ret) {
947 pthread_mutex_unlock(&ps->lock);
948 }
949
950 return ret;
951}
952
Michal Vasko428087d2016-01-14 16:04:28 +0100953API struct nc_pollsession *
954nc_ps_new(void)
955{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100956 struct nc_pollsession *ps;
957
958 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100959 if (!ps) {
960 ERRMEM;
961 return NULL;
962 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200963 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100964 pthread_mutex_init(&ps->lock, NULL);
965
966 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100967}
968
969API void
970nc_ps_free(struct nc_pollsession *ps)
971{
fanchanghu3d4e7212017-08-09 09:42:30 +0800972 uint16_t i;
973
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100974 if (!ps) {
975 return;
976 }
977
Michal Vaskobe86fe32016-04-07 10:43:03 +0200978 if (ps->queue_len) {
979 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
980 }
981
fanchanghu3d4e7212017-08-09 09:42:30 +0800982 for (i = 0; i < ps->session_count; i++) {
983 free(ps->sessions[i]);
984 }
985
Michal Vasko428087d2016-01-14 16:04:28 +0100986 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100987 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200988 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100989
Michal Vasko428087d2016-01-14 16:04:28 +0100990 free(ps);
991}
992
993API int
994nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
995{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200996 uint8_t q_id;
997
Michal Vasko45e53ae2016-04-07 11:46:03 +0200998 if (!ps) {
999 ERRARG("ps");
1000 return -1;
1001 } else if (!session) {
1002 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001003 return -1;
1004 }
1005
Michal Vasko48a63ed2016-03-01 09:48:21 +01001006 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001007 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001008 return -1;
1009 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001010
Michal Vasko428087d2016-01-14 16:04:28 +01001011 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001012 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001013 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001014 ERRMEM;
1015 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001016 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001017 return -1;
1018 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001019 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1020 if (!ps->sessions[ps->session_count - 1]) {
1021 ERRMEM;
1022 --ps->session_count;
1023 /* UNLOCK */
1024 nc_ps_unlock(ps, q_id, __func__);
1025 return -1;
1026 }
1027 ps->sessions[ps->session_count - 1]->session = session;
1028 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001029
Michal Vasko48a63ed2016-03-01 09:48:21 +01001030 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001031 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001032}
1033
Michal Vasko48a63ed2016-03-01 09:48:21 +01001034static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001035_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001036{
1037 uint16_t i;
1038
Radek Krejcid5f978f2016-03-03 13:14:45 +01001039 if (index >= 0) {
1040 i = (uint16_t)index;
1041 goto remove;
1042 }
Michal Vasko428087d2016-01-14 16:04:28 +01001043 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001044 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001045remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001046 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001047 if (i <= ps->session_count) {
1048 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001049 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001050 }
1051 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001052 free(ps->sessions);
1053 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001054 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001055 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001056 return 0;
1057 }
1058 }
1059
Michal Vaskof0537d82016-01-29 14:42:38 +01001060 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001061}
1062
Michal Vasko48a63ed2016-03-01 09:48:21 +01001063API int
1064nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1065{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001066 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001067 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001068
Michal Vasko45e53ae2016-04-07 11:46:03 +02001069 if (!ps) {
1070 ERRARG("ps");
1071 return -1;
1072 } else if (!session) {
1073 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001074 return -1;
1075 }
1076
1077 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001078 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001079 return -1;
1080 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001081
Radek Krejcid5f978f2016-03-03 13:14:45 +01001082 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001083
1084 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001085 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001086
Michal Vaskobe86fe32016-04-07 10:43:03 +02001087 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001088}
1089
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001090API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001091nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001092{
1093 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001094 struct nc_session *ret = NULL;
1095
1096 if (!ps) {
1097 ERRARG("ps");
1098 return NULL;
1099 }
1100
1101 /* LOCK */
1102 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1103 return NULL;
1104 }
1105
Michal Vasko4871c9d2017-10-09 14:48:39 +02001106 if (idx < ps->session_count) {
1107 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001108 }
1109
1110 /* UNLOCK */
1111 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1112
1113 return ret;
1114}
1115
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001116API uint16_t
1117nc_ps_session_count(struct nc_pollsession *ps)
1118{
Michal Vasko47003942019-03-14 12:25:23 +01001119 uint8_t q_id;
1120 uint16_t session_count;
1121
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001122 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001123 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001124 return 0;
1125 }
1126
Michal Vasko47003942019-03-14 12:25:23 +01001127 /* LOCK (just for memory barrier so that we read the current value) */
1128 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1129 return 0;
1130 }
1131
1132 session_count = ps->session_count;
1133
1134 /* UNLOCK */
1135 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1136
1137 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001138}
1139
Michal Vasko131120a2018-05-29 15:44:02 +02001140/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001141 * returns: NC_PSPOLL_ERROR,
1142 * NC_PSPOLL_BAD_RPC,
1143 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1144 * NC_PSPOLL_RPC
1145 */
1146static int
Michal Vasko131120a2018-05-29 15:44:02 +02001147nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001148{
1149 struct lyxml_elem *xml = NULL;
1150 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001151 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001152 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001153
Michal Vasko45e53ae2016-04-07 11:46:03 +02001154 if (!session) {
1155 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001156 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001157 } else if (!rpc) {
1158 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001159 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001160 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001161 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001162 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001163 }
1164
Michal Vasko131120a2018-05-29 15:44:02 +02001165 msgtype = nc_read_msg_io(session, io_timeout, &xml, 0);
Michal Vasko428087d2016-01-14 16:04:28 +01001166
1167 switch (msgtype) {
1168 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001169 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001170 if (!*rpc) {
1171 ERRMEM;
1172 goto error;
1173 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001174
Radek Krejcif93c7d42016-04-06 13:41:15 +02001175 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001176 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1177 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001178 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001179 /* parsing RPC failed */
Michal Vaskoc9970242018-02-14 16:03:35 +01001180 reply = nc_server_reply_err(nc_err_libyang(server_opts.ctx));
Michal Vasko131120a2018-05-29 15:44:02 +02001181 ret = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001182 nc_server_reply_free(reply);
1183 if (ret == -1) {
1184 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001185 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001186 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1187 } else {
1188 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001189 }
Michal Vasko428087d2016-01-14 16:04:28 +01001190 (*rpc)->root = xml;
1191 break;
1192 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001193 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001194 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001195 goto error;
1196 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001197 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001198 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001199 goto error;
1200 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001201 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001202 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001203 goto error;
1204 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001205 /* NC_MSG_ERROR,
Michal Vasko131120a2018-05-29 15:44:02 +02001206 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg_io()
Michal Vasko428087d2016-01-14 16:04:28 +01001207 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001208 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001209 break;
1210 }
1211
Michal Vasko71090fc2016-05-24 16:37:28 +02001212 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001213
1214error:
1215 /* cleanup */
1216 lyxml_free(server_opts.ctx, xml);
1217
Michal Vasko71090fc2016-05-24 16:37:28 +02001218 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001219}
1220
fanchanghu966f2de2016-07-21 02:28:57 -04001221API void
1222nc_set_global_rpc_clb(nc_rpc_clb clb)
1223{
1224 global_rpc_clb = clb;
1225}
1226
Radek Krejci93e80222016-10-03 13:34:25 +02001227API NC_MSG_TYPE
1228nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1229{
Michal Vasko131120a2018-05-29 15:44:02 +02001230 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001231
1232 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001233 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001234 ERRARG("session");
1235 return NC_MSG_ERROR;
1236 } else if (!notif || !notif->tree || !notif->eventtime) {
1237 ERRARG("notif");
1238 return NC_MSG_ERROR;
1239 }
1240
Michal Vasko131120a2018-05-29 15:44:02 +02001241 /* we do not need RPC lock for this, IO lock will be acquired properly */
1242 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
1243 if (ret == NC_MSG_ERROR) {
Radek Krejci93e80222016-10-03 13:34:25 +02001244 ERR("Session %u: failed to write notification.", session->id);
Radek Krejci93e80222016-10-03 13:34:25 +02001245 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001246
Michal Vasko131120a2018-05-29 15:44:02 +02001247 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001248}
1249
Michal Vasko131120a2018-05-29 15:44:02 +02001250/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001251 * returns: NC_PSPOLL_ERROR,
1252 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1253 * NC_PSPOLL_REPLY_ERROR,
1254 * 0
1255 */
1256static int
Michal Vasko131120a2018-05-29 15:44:02 +02001257nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001258{
1259 nc_rpc_clb clb;
1260 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001261 struct lys_node *rpc_act = NULL;
1262 struct lyd_node *next, *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001263 int ret = 0;
1264 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001265
Michal Vasko4a827e52016-03-03 10:59:00 +01001266 if (!rpc) {
1267 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001268 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001269 }
1270
Michal Vasko90e8e692016-07-13 12:27:57 +02001271 if (rpc->tree->schema->nodetype == LYS_RPC) {
1272 /* RPC */
1273 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001274 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001275 /* action */
1276 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1277 if (elem->schema->nodetype == LYS_ACTION) {
1278 rpc_act = elem->schema;
1279 break;
1280 }
1281 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001282 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001283 if (!rpc_act) {
1284 ERRINT;
1285 return NC_PSPOLL_ERROR;
1286 }
1287 }
1288
1289 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001290 if (!global_rpc_clb) {
1291 /* no callback, reply with a not-implemented error */
1292 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
1293 } else {
1294 reply = global_rpc_clb(rpc->tree, session);
1295 }
Michal Vasko428087d2016-01-14 16:04:28 +01001296 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001297 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001298 reply = clb(rpc->tree, session);
1299 }
1300
1301 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001302 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001303 }
Michal Vasko131120a2018-05-29 15:44:02 +02001304 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->root, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001305 if (reply->type == NC_RPL_ERROR) {
1306 ret |= NC_PSPOLL_REPLY_ERROR;
1307 }
1308 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001309
Michal Vasko131120a2018-05-29 15:44:02 +02001310 if (r != NC_MSG_REPLY) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001311 ERR("Session %u: failed to write reply.", session->id);
1312 ret |= NC_PSPOLL_ERROR;
1313 }
Michal Vasko428087d2016-01-14 16:04:28 +01001314
1315 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1316 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1317 session->status = NC_STATUS_INVALID;
1318 }
1319
Michal Vasko71090fc2016-05-24 16:37:28 +02001320 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001321}
1322
Michal Vasko131120a2018-05-29 15:44:02 +02001323/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001324 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1325 * NC_PSPOLL_ERROR, (msg filled)
1326 * NC_PSPOLL_TIMEOUT,
1327 * NC_PSPOLL_RPC (some application data available),
1328 * NC_PSPOLL_SSH_CHANNEL,
1329 * NC_PSPOLL_SSH_MSG
1330 */
1331static int
Michal Vasko131120a2018-05-29 15:44:02 +02001332nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001333{
Michal Vasko9a327362017-01-11 11:31:46 +01001334 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001335 int r, ret = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001336#ifdef NC_ENABLED_SSH
1337 struct nc_session *new;
1338#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001339
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001340 /* check timeout first */
1341 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001342 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001343 sprintf(msg, "session idle timeout elapsed");
1344 session->status = NC_STATUS_INVALID;
1345 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1346 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1347 }
1348
Michal Vasko131120a2018-05-29 15:44:02 +02001349 r = nc_session_io_lock(session, io_timeout, __func__);
1350 if (r < 0) {
1351 sprintf(msg, "session IO lock failed to be acquired");
1352 return NC_PSPOLL_ERROR;
1353 } else if (!r) {
1354 return NC_PSPOLL_TIMEOUT;
1355 }
1356
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001357 switch (session->ti_type) {
1358#ifdef NC_ENABLED_SSH
1359 case NC_TI_LIBSSH:
1360 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001361 if (r == SSH_EOF) {
1362 sprintf(msg, "SSH channel unexpected EOF");
1363 session->status = NC_STATUS_INVALID;
1364 session->term_reason = NC_SESSION_TERM_DROPPED;
1365 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1366 } else if (r == SSH_ERROR) {
1367 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001368 session->status = NC_STATUS_INVALID;
1369 session->term_reason = NC_SESSION_TERM_OTHER;
1370 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001371 } else if (!r) {
1372 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1373 /* new SSH message */
1374 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1375 if (session->ti.libssh.next) {
1376 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1377 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1378 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1379 /* new NETCONF SSH channel */
1380 ret = NC_PSPOLL_SSH_CHANNEL;
1381 break;
1382 }
1383 }
1384 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001385 break;
1386 }
1387 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001388
Michal Vasko8dcaa882017-10-19 14:28:42 +02001389 /* just some SSH message */
1390 ret = NC_PSPOLL_SSH_MSG;
1391 } else {
1392 ret = NC_PSPOLL_TIMEOUT;
1393 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001394 } else {
1395 /* we have some application data */
1396 ret = NC_PSPOLL_RPC;
1397 }
1398 break;
1399#endif
1400#ifdef NC_ENABLED_TLS
1401 case NC_TI_OPENSSL:
1402 r = SSL_pending(session->ti.tls);
1403 if (!r) {
1404 /* no data pending in the SSL buffer, poll fd */
1405 pfd.fd = SSL_get_rfd(session->ti.tls);
1406 if (pfd.fd < 0) {
1407 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1408 ret = NC_PSPOLL_ERROR;
1409 break;
1410 }
1411 pfd.events = POLLIN;
1412 pfd.revents = 0;
1413 r = poll(&pfd, 1, 0);
1414
1415 if ((r < 0) && (errno != EINTR)) {
1416 sprintf(msg, "poll failed (%s)", strerror(errno));
1417 session->status = NC_STATUS_INVALID;
1418 ret = NC_PSPOLL_ERROR;
1419 } else if (r > 0) {
1420 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1421 sprintf(msg, "communication socket unexpectedly closed");
1422 session->status = NC_STATUS_INVALID;
1423 session->term_reason = NC_SESSION_TERM_DROPPED;
1424 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1425 } else if (pfd.revents & POLLERR) {
1426 sprintf(msg, "communication socket error");
1427 session->status = NC_STATUS_INVALID;
1428 session->term_reason = NC_SESSION_TERM_OTHER;
1429 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1430 } else {
1431 ret = NC_PSPOLL_RPC;
1432 }
1433 } else {
1434 ret = NC_PSPOLL_TIMEOUT;
1435 }
1436 } else {
1437 ret = NC_PSPOLL_RPC;
1438 }
1439 break;
1440#endif
1441 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001442 case NC_TI_UNIX:
1443 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001444 pfd.events = POLLIN;
1445 pfd.revents = 0;
1446 r = poll(&pfd, 1, 0);
1447
1448 if ((r < 0) && (errno != EINTR)) {
1449 sprintf(msg, "poll failed (%s)", strerror(errno));
1450 session->status = NC_STATUS_INVALID;
1451 ret = NC_PSPOLL_ERROR;
1452 } else if (r > 0) {
1453 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1454 sprintf(msg, "communication socket unexpectedly closed");
1455 session->status = NC_STATUS_INVALID;
1456 session->term_reason = NC_SESSION_TERM_DROPPED;
1457 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1458 } else if (pfd.revents & POLLERR) {
1459 sprintf(msg, "communication socket error");
1460 session->status = NC_STATUS_INVALID;
1461 session->term_reason = NC_SESSION_TERM_OTHER;
1462 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1463 } else {
1464 ret = NC_PSPOLL_RPC;
1465 }
1466 } else {
1467 ret = NC_PSPOLL_TIMEOUT;
1468 }
1469 break;
1470 case NC_TI_NONE:
1471 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1472 ret = NC_PSPOLL_ERROR;
1473 break;
1474 }
1475
Michal Vasko131120a2018-05-29 15:44:02 +02001476 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001477 return ret;
1478}
1479
1480API int
1481nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1482{
1483 int ret, r;
1484 uint8_t q_id;
1485 uint16_t i, j;
1486 char msg[256];
1487 struct timespec ts_timeout, ts_cur;
1488 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001489 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001490 struct nc_server_rpc *rpc = NULL;
1491
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001492 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001493 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001494 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001495 }
1496
Michal Vaskoade892d2017-02-22 13:40:35 +01001497 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001498 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001499 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001500 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001501
Michal Vaskoade892d2017-02-22 13:40:35 +01001502 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001503 nc_ps_unlock(ps, q_id, __func__);
1504 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001505 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001506
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001507 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001508 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001509 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001510 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001511 nc_addtimespec(&ts_timeout, timeout);
1512 }
1513
1514 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001515 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001516 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001517 if (ps->last_event_session == ps->session_count - 1) {
1518 i = j = 0;
1519 } else {
1520 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001521 }
Michal Vasko9a327362017-01-11 11:31:46 +01001522 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001523 cur_ps_session = ps->sessions[i];
1524 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001525
Michal Vasko131120a2018-05-29 15:44:02 +02001526 /* SESSION RPC LOCK */
1527 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001528 if (r == -1) {
1529 ret = NC_PSPOLL_ERROR;
1530 } else if (r == 1) {
1531 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001532 switch (cur_ps_session->state) {
1533 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001534 if (cur_session->status == NC_STATUS_RUNNING) {
1535 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001536 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001537
Michal Vasko131120a2018-05-29 15:44:02 +02001538 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001539 switch (ret) {
1540 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1541 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001542 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001543 break;
1544 case NC_PSPOLL_ERROR:
1545 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001546 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001547 break;
1548 case NC_PSPOLL_TIMEOUT:
1549#ifdef NC_ENABLED_SSH
1550 case NC_PSPOLL_SSH_CHANNEL:
1551 case NC_PSPOLL_SSH_MSG:
1552#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001553 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001554 break;
1555 case NC_PSPOLL_RPC:
1556 /* let's keep the state busy, we are not done with this session */
1557 break;
1558 }
1559 } else {
1560 /* session is not fine, let the caller know */
1561 ret = NC_PSPOLL_SESSION_TERM;
1562 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1563 ret |= NC_PSPOLL_SESSION_ERROR;
1564 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001565 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001566 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001567 break;
1568 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001569 /* it definitely should not be busy because we have the lock */
1570 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001571 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001572 break;
1573 case NC_PS_STATE_INVALID:
1574 /* we got it locked, but it will be freed, let it be */
1575 ret = NC_PSPOLL_TIMEOUT;
1576 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001577 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001578
Michal Vasko131120a2018-05-29 15:44:02 +02001579 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001580 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001581 /* SESSION RPC UNLOCK */
1582 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001583 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001584 } else {
1585 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001586 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001587 }
Michal Vasko428087d2016-01-14 16:04:28 +01001588
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001589 /* something happened */
1590 if (ret != NC_PSPOLL_TIMEOUT) {
1591 break;
1592 }
1593
Michal Vasko9a327362017-01-11 11:31:46 +01001594 if (i == ps->session_count - 1) {
1595 i = 0;
1596 } else {
1597 ++i;
1598 }
1599 } while (i != j);
1600
Michal Vaskoade892d2017-02-22 13:40:35 +01001601 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001602 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001603 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001604 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001605 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001606
1607 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1608 /* final timeout */
1609 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001610 }
Michal Vasko428087d2016-01-14 16:04:28 +01001611 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001612 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001613
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001614 /* do we want to return the session? */
1615 switch (ret) {
1616 case NC_PSPOLL_RPC:
1617 case NC_PSPOLL_SESSION_TERM:
1618 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1619#ifdef NC_ENABLED_SSH
1620 case NC_PSPOLL_SSH_CHANNEL:
1621 case NC_PSPOLL_SSH_MSG:
1622#endif
1623 if (session) {
1624 *session = cur_session;
1625 }
1626 ps->last_event_session = i;
1627 break;
1628 default:
1629 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001630 }
Michal Vasko428087d2016-01-14 16:04:28 +01001631
Michal Vaskoade892d2017-02-22 13:40:35 +01001632 /* PS UNLOCK */
1633 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001634
Michal Vasko131120a2018-05-29 15:44:02 +02001635 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001636 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001637 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001638 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1639 if (cur_session->status != NC_STATUS_RUNNING) {
1640 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001641 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001642 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001643 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001644 }
1645 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001646 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001647
Michal Vasko7f1ee932018-10-11 09:41:42 +02001648 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001649 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001650 if (cur_session->status != NC_STATUS_RUNNING) {
1651 ret |= NC_PSPOLL_SESSION_TERM;
1652 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1653 ret |= NC_PSPOLL_SESSION_ERROR;
1654 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001655 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001656 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001657 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001658 }
Michal Vasko428087d2016-01-14 16:04:28 +01001659 }
Michal Vasko7f1ee932018-10-11 09:41:42 +02001660 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001661
Michal Vasko131120a2018-05-29 15:44:02 +02001662 /* SESSION RPC UNLOCK */
1663 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001664 }
1665
Michal Vasko48a63ed2016-03-01 09:48:21 +01001666 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001667}
1668
Michal Vaskod09eae62016-02-01 10:32:52 +01001669API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001670nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001671{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001672 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001673 uint16_t i;
1674 struct nc_session *session;
1675
Michal Vasko9a25e932016-02-01 10:36:42 +01001676 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001677 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001678 return;
1679 }
1680
Michal Vasko48a63ed2016-03-01 09:48:21 +01001681 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001682 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001683 return;
1684 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001685
Michal Vasko48a63ed2016-03-01 09:48:21 +01001686 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001687 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001688 nc_session_free(ps->sessions[i]->session, data_free);
1689 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001690 }
1691 free(ps->sessions);
1692 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001693 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001694 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001695 } else {
1696 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001697 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1698 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001699 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001700 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001701 continue;
1702 }
1703
1704 ++i;
1705 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001706 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001707
1708 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001709 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001710}
1711
Radek Krejci53691be2016-02-22 13:58:37 +01001712#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001713
Michal Vasko5f352c52019-07-10 16:12:06 +02001714static int
1715nc_accept_unix(struct nc_session *session, int sock)
1716{
1717 const struct passwd *pw;
1718 struct ucred ucred;
1719 char *username;
1720 socklen_t len;
1721
1722 session->ti_type = NC_TI_UNIX;
1723
1724 len = sizeof(ucred);
1725 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
1726 ERR("Failed to get credentials from unix socket (%s).",
1727 strerror(errno));
1728 close(sock);
1729 return -1;
1730 }
1731
1732 pw = getpwuid(ucred.uid);
1733 if (pw == NULL) {
1734 ERR("Failed to find username for uid=%u (%s).\n", ucred.uid,
1735 strerror(errno));
1736 close(sock);
1737 return -1;
1738 }
1739
1740 username = strdup(pw->pw_name);
1741 if (username == NULL) {
1742 ERRMEM;
1743 close(sock);
1744 return -1;
1745 }
1746 session->username = lydict_insert_zc(server_opts.ctx, username);
1747
1748 session->ti.unixsock.sock = sock;
1749
1750 return 1;
1751}
1752
Michal Vaskoe2713da2016-08-22 16:06:40 +02001753API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001754nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001755{
Michal Vasko3031aae2016-01-27 16:07:18 +01001756 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001757 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001758
Michal Vasko45e53ae2016-04-07 11:46:03 +02001759 if (!name) {
1760 ERRARG("name");
1761 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001762 }
1763
Michal Vaskoade892d2017-02-22 13:40:35 +01001764 /* BIND LOCK */
1765 pthread_mutex_lock(&server_opts.bind_lock);
1766
1767 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001768 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001769
1770 /* check name uniqueness */
1771 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001772 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001773 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001774 ret = -1;
1775 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001776 }
1777 }
1778
Michal Vasko3031aae2016-01-27 16:07:18 +01001779 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001780 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001781 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001782 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001783 ret = -1;
1784 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001785 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001786 memset(&server_opts.endpts[server_opts.endpt_count - 1], 0, sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001787 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001788 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001789 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
1790 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
1791 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001792
Michal Vaskoe2713da2016-08-22 16:06:40 +02001793 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001794 if (!server_opts.binds) {
1795 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001796 ret = -1;
1797 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001798 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001799
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001800 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001801 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1802
1803 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001804#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001805 case NC_TI_LIBSSH:
1806 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1807 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1808 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001809 ret = -1;
1810 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001811 }
1812 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1813 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1814 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02001815 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001816 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001817#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001818#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001819 case NC_TI_OPENSSL:
1820 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1821 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1822 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001823 ret = -1;
1824 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001825 }
1826 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001827#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001828 case NC_TI_UNIX:
1829 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1830 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1831 ERRMEM;
1832 ret = -1;
1833 goto cleanup;
1834 }
1835 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1836 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1837 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1838 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001839 default:
1840 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001841 ret = -1;
1842 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001843 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001844
Michal Vaskoade892d2017-02-22 13:40:35 +01001845cleanup:
1846 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001847 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001848
Michal Vaskoade892d2017-02-22 13:40:35 +01001849 /* BIND UNLOCK */
1850 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001851
Michal Vaskoade892d2017-02-22 13:40:35 +01001852 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001853}
1854
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001855API int
Michal Vasko59050372016-11-22 14:33:55 +01001856nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001857{
1858 uint32_t i;
1859 int ret = -1;
1860
Michal Vaskoade892d2017-02-22 13:40:35 +01001861 /* BIND LOCK */
1862 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001863
Michal Vaskoade892d2017-02-22 13:40:35 +01001864 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001865 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001866
Michal Vasko59050372016-11-22 14:33:55 +01001867 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001868 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001869 for (i = 0; i < server_opts.endpt_count; ++i) {
1870 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001871 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001872#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001873 case NC_TI_LIBSSH:
1874 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1875 free(server_opts.endpts[i].opts.ssh);
1876 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001877#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001878#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001879 case NC_TI_OPENSSL:
1880 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1881 free(server_opts.endpts[i].opts.tls);
1882 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001883#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001884 case NC_TI_UNIX:
1885 free(server_opts.endpts[i].opts.unixsock);
1886 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001887 default:
1888 ERRINT;
1889 /* won't get here ...*/
1890 break;
1891 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001892 ret = 0;
1893 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001894 free(server_opts.endpts);
1895 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001896
1897 /* remove all binds */
1898 for (i = 0; i < server_opts.endpt_count; ++i) {
1899 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1900 if (server_opts.binds[i].sock > -1) {
1901 close(server_opts.binds[i].sock);
1902 }
1903 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001904 free(server_opts.binds);
1905 server_opts.binds = NULL;
1906
Michal Vasko3031aae2016-01-27 16:07:18 +01001907 server_opts.endpt_count = 0;
1908
Michal Vasko1a38c862016-01-15 15:50:07 +01001909 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001910 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001911 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001912 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001913 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001914 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001915 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001916#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001917 case NC_TI_LIBSSH:
1918 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1919 free(server_opts.endpts[i].opts.ssh);
1920 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001921#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001922#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001923 case NC_TI_OPENSSL:
1924 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1925 free(server_opts.endpts[i].opts.tls);
1926 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001927#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001928 case NC_TI_UNIX:
1929 free(server_opts.endpts[i].opts.unixsock);
1930 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001931 default:
1932 ERRINT;
1933 break;
1934 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001935
Michal Vaskoe2713da2016-08-22 16:06:40 +02001936 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001937 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1938 if (server_opts.binds[i].sock > -1) {
1939 close(server_opts.binds[i].sock);
1940 }
1941
1942 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001943 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001944 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001945 free(server_opts.binds);
1946 server_opts.binds = NULL;
1947 free(server_opts.endpts);
1948 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001949 } else if (i < server_opts.endpt_count) {
1950 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1951 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001952 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001953
1954 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001955 if (name) {
1956 break;
1957 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001958 }
1959 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001960 }
1961
Michal Vaskoade892d2017-02-22 13:40:35 +01001962 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001963 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001964
Michal Vaskoade892d2017-02-22 13:40:35 +01001965 /* BIND UNLOCK */
1966 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001967
1968 return ret;
1969}
1970
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001971API int
1972nc_server_endpt_count(void)
1973{
1974 return server_opts.endpt_count;
1975}
1976
1977int
1978nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
1979{
1980 struct nc_endpt *endpt;
1981 struct nc_bind *bind = NULL;
1982 uint16_t i;
1983 int sock = -1, set_addr, ret = 0;
1984
1985 if (!endpt_name) {
1986 ERRARG("endpt_name");
1987 return -1;
1988 } else if ((!address && !port) || (address && port)) {
1989 ERRARG("address and port");
1990 return -1;
1991 }
1992
1993 if (address) {
1994 set_addr = 1;
1995 } else {
1996 set_addr = 0;
1997 }
1998
1999 /* BIND LOCK */
2000 pthread_mutex_lock(&server_opts.bind_lock);
2001
2002 /* ENDPT LOCK */
2003 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2004 if (!endpt) {
2005 /* BIND UNLOCK */
2006 pthread_mutex_unlock(&server_opts.bind_lock);
2007 return -1;
2008 }
2009
2010 bind = &server_opts.binds[i];
2011
2012 if (set_addr) {
2013 port = bind->port;
2014 } else {
2015 address = bind->address;
2016 }
2017
2018 if (!set_addr && endpt->ti == NC_TI_UNIX) {
2019 ret = -1;
2020 goto cleanup;
2021 }
2022
2023 /* we have all the information we need to create a listening socket */
2024 if (address && (port || endpt->ti == NC_TI_UNIX)) {
2025 /* create new socket, close the old one */
2026 if (endpt->ti == NC_TI_UNIX)
2027 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
2028 else
2029 sock = nc_sock_listen_inet(address, port, &endpt->ka);
2030 if (sock == -1) {
2031 ret = -1;
2032 goto cleanup;
2033 }
2034
2035 if (bind->sock > -1) {
2036 close(bind->sock);
2037 }
2038 bind->sock = sock;
2039 } /* else we are just setting address or port */
2040
2041 if (set_addr) {
2042 lydict_remove(server_opts.ctx, bind->address);
2043 bind->address = lydict_insert(server_opts.ctx, address, 0);
2044 } else {
2045 bind->port = port;
2046 }
2047
2048 if (sock > -1) {
2049#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
2050 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
2051#elif defined(NC_ENABLED_SSH)
2052 VRB("Listening on %s:%u for SSH connections.", address, port);
2053#else
2054 VRB("Listening on %s:%u for TLS connections.", address, port);
2055#endif
2056 }
2057
2058cleanup:
2059 /* ENDPT UNLOCK */
2060 pthread_rwlock_unlock(&server_opts.endpt_lock);
2061
2062 /* BIND UNLOCK */
2063 pthread_mutex_unlock(&server_opts.bind_lock);
2064
2065 return ret;
2066}
2067
2068API int
2069nc_server_endpt_set_address(const char *endpt_name, const char *address)
2070{
2071 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2072}
2073
2074API int
2075nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2076{
2077 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2078}
2079
2080API int
2081nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2082{
2083 struct nc_endpt *endpt;
2084 uint16_t i;
2085 int ret = 0;
2086
2087 if (!endpt_name) {
2088 ERRARG("endpt_name");
2089 return -1;
2090 } else if (mode == 0) {
2091 ERRARG("mode");
2092 return -1;
2093 }
2094
2095 /* ENDPT LOCK */
2096 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2097 if (!endpt)
2098 return -1;
2099
2100 if (endpt->ti != NC_TI_UNIX) {
2101 ret = -1;
2102 goto cleanup;
2103 }
2104
2105 endpt->opts.unixsock->mode = mode;
2106 endpt->opts.unixsock->uid = uid;
2107 endpt->opts.unixsock->gid = gid;
2108
2109cleanup:
2110 /* ENDPT UNLOCK */
2111 pthread_rwlock_unlock(&server_opts.endpt_lock);
2112
2113 return ret;
2114}
2115
2116API int
2117nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2118{
2119 struct nc_endpt *endpt;
2120 int ret = 0;
2121
2122 if (!endpt_name) {
2123 ERRARG("endpt_name");
2124 return -1;
2125 }
2126
2127 /* ENDPT LOCK */
2128 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2129 if (!endpt) {
2130 return -1;
2131 }
2132
2133 endpt->ka.enabled = (enable ? 1 : 0);
2134
2135 /* ENDPT UNLOCK */
2136 pthread_rwlock_unlock(&server_opts.endpt_lock);
2137
2138 return ret;
2139}
2140
2141API int
2142nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2143{
2144 struct nc_endpt *endpt;
2145 int ret = 0;
2146
2147 if (!endpt_name) {
2148 ERRARG("endpt_name");
2149 return -1;
2150 }
2151
2152 /* ENDPT LOCK */
2153 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2154 if (!endpt) {
2155 return -1;
2156 }
2157
2158 if (idle_time > -1) {
2159 endpt->ka.idle_time = idle_time;
2160 }
2161 if (max_probes > -1) {
2162 endpt->ka.max_probes = max_probes;
2163 }
2164 if (probe_interval > -1) {
2165 endpt->ka.probe_interval = probe_interval;
2166 }
2167
2168 /* ENDPT UNLOCK */
2169 pthread_rwlock_unlock(&server_opts.endpt_lock);
2170
2171 return ret;
2172}
2173
Michal Vasko71090fc2016-05-24 16:37:28 +02002174API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002175nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002176{
Michal Vasko71090fc2016-05-24 16:37:28 +02002177 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002178 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002179 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002180 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002181 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002182
Michal Vasko45e53ae2016-04-07 11:46:03 +02002183 if (!server_opts.ctx) {
2184 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002185 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002186 } else if (!session) {
2187 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002188 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002189 }
2190
Michal Vaskoade892d2017-02-22 13:40:35 +01002191 /* BIND LOCK */
2192 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002193
2194 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02002195 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002196 /* BIND UNLOCK */
2197 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002198 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002199 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002200
Michal Vaskoe2713da2016-08-22 16:06:40 +02002201 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002202 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002203 /* BIND UNLOCK */
2204 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002205 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002206 if (!ret) {
2207 return NC_MSG_WOULDBLOCK;
2208 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002209 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002210 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002211
2212 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2213 /* ENDPT READ LOCK */
2214 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2215
2216 /* BIND UNLOCK */
2217 pthread_mutex_unlock(&server_opts.bind_lock);
2218
Michal Vaskob48aa812016-01-18 14:13:09 +01002219 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002220
Michal Vasko131120a2018-05-29 15:44:02 +02002221 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002222 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002223 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002224 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002225 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002226 msgtype = NC_MSG_ERROR;
2227 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002228 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002229 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002230 (*session)->ctx = server_opts.ctx;
2231 (*session)->flags = NC_SESSION_SHAREDCTX;
2232 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
2233 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002234
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002235 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002236#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002237 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2238 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002239 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002240 if (ret < 0) {
2241 msgtype = NC_MSG_ERROR;
2242 goto cleanup;
2243 } else if (!ret) {
2244 msgtype = NC_MSG_WOULDBLOCK;
2245 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002246 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002247 } else
2248#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002249#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002250 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2251 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002252 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002253 if (ret < 0) {
2254 msgtype = NC_MSG_ERROR;
2255 goto cleanup;
2256 } else if (!ret) {
2257 msgtype = NC_MSG_WOULDBLOCK;
2258 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002259 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002260 } else
2261#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002262 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2263 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2264 ret = nc_accept_unix(*session, sock);
2265 if (ret < 0) {
2266 msgtype = NC_MSG_ERROR;
2267 goto cleanup;
2268 }
2269 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002270 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002271 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002272 msgtype = NC_MSG_ERROR;
2273 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002274 }
2275
Michal Vasko2cc4c682016-03-01 09:16:48 +01002276 (*session)->data = NULL;
2277
Michal Vaskoade892d2017-02-22 13:40:35 +01002278 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002279 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002280
Michal Vaskob48aa812016-01-18 14:13:09 +01002281 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002282 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002283
Michal Vasko9e036d52016-01-08 10:49:26 +01002284 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002285 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002286 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002287 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002288 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002289 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002290 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002291
2292 nc_gettimespec_mono(&ts_cur);
2293 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2294 nc_gettimespec_real(&ts_cur);
2295 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002296 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002297
Michal Vasko71090fc2016-05-24 16:37:28 +02002298 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002299
Michal Vasko71090fc2016-05-24 16:37:28 +02002300cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002301 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002302 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002303
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002304 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002305 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002306 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002307}
2308
Michal Vaskoadf30f02019-06-24 09:34:47 +02002309/* client is expected to be locked */
2310static int
2311_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 +02002312{
2313 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002314 int ret = -1;
2315
2316 if (!endpt_name) {
2317 /* remove all endpoints */
2318 for (i = 0; i < client->ch_endpt_count; ++i) {
2319 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2320 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2321 if (client->ch_endpts[i].sock_pending != -1) {
2322 close(client->ch_endpts[i].sock_pending);
2323 }
2324 switch (client->ch_endpts[i].ti) {
2325#ifdef NC_ENABLED_SSH
2326 case NC_TI_LIBSSH:
2327 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2328 free(client->ch_endpts[i].opts.ssh);
2329 break;
2330#endif
2331#ifdef NC_ENABLED_TLS
2332 case NC_TI_OPENSSL:
2333 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2334 free(client->ch_endpts[i].opts.tls);
2335 break;
2336#endif
2337 default:
2338 ERRINT;
2339 /* won't get here ...*/
2340 break;
2341 }
2342 }
2343 free(client->ch_endpts);
2344 client->ch_endpts = NULL;
2345 client->ch_endpt_count = 0;
2346
2347 ret = 0;
2348 } else {
2349 for (i = 0; i < client->ch_endpt_count; ++i) {
2350 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
2351 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2352 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2353 if (client->ch_endpts[i].sock_pending != -1) {
2354 close(client->ch_endpts[i].sock_pending);
2355 }
2356 switch (client->ch_endpts[i].ti) {
2357#ifdef NC_ENABLED_SSH
2358 case NC_TI_LIBSSH:
2359 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2360 free(client->ch_endpts[i].opts.ssh);
2361 break;
2362#endif
2363#ifdef NC_ENABLED_TLS
2364 case NC_TI_OPENSSL:
2365 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2366 free(client->ch_endpts[i].opts.tls);
2367 break;
2368#endif
2369 default:
2370 ERRINT;
2371 /* won't get here ...*/
2372 break;
2373 }
2374
2375 /* move last endpoint to the empty space */
2376 --client->ch_endpt_count;
2377 if (i < client->ch_endpt_count) {
2378 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2379 } else if (!server_opts.ch_client_count) {
2380 free(server_opts.ch_clients);
2381 server_opts.ch_clients = NULL;
2382 }
2383
2384 ret = 0;
2385 break;
2386 }
2387 }
2388 }
2389
2390 return ret;
2391}
2392
2393API int
2394nc_server_ch_add_client(const char *name)
2395{
2396 uint16_t i;
2397 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002398
2399 if (!name) {
2400 ERRARG("name");
2401 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002402 }
2403
2404 /* WRITE LOCK */
2405 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2406
2407 /* check name uniqueness */
2408 for (i = 0; i < server_opts.ch_client_count; ++i) {
2409 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2410 ERR("Call Home client \"%s\" already exists.", name);
2411 /* WRITE UNLOCK */
2412 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2413 return -1;
2414 }
2415 }
2416
2417 ++server_opts.ch_client_count;
2418 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2419 if (!server_opts.ch_clients) {
2420 ERRMEM;
2421 /* WRITE UNLOCK */
2422 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2423 return -1;
2424 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002425 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002426
Michal Vaskoadf30f02019-06-24 09:34:47 +02002427 client->name = lydict_insert(server_opts.ctx, name, 0);
2428 client->id = ATOMIC_INC(&server_opts.new_client_id);
2429 client->ch_endpts = NULL;
2430 client->ch_endpt_count = 0;
2431 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002432
Michal Vasko2e6defd2016-10-07 15:48:15 +02002433 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002434 client->start_with = NC_CH_FIRST_LISTED;
2435 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002436
Michal Vaskoadf30f02019-06-24 09:34:47 +02002437 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002438
2439 /* WRITE UNLOCK */
2440 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2441
2442 return 0;
2443}
2444
2445API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002446nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002447{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002448 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002449 int ret = -1;
2450
2451 /* WRITE LOCK */
2452 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2453
Michal Vaskoadf30f02019-06-24 09:34:47 +02002454 if (!name) {
2455 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002456 for (i = 0; i < server_opts.ch_client_count; ++i) {
2457 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2458
2459 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002460 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002461
2462 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002463 ret = 0;
2464 }
2465 free(server_opts.ch_clients);
2466 server_opts.ch_clients = NULL;
2467
2468 server_opts.ch_client_count = 0;
2469
2470 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002471 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002472 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002473 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002474 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2475
Michal Vasko2e6defd2016-10-07 15:48:15 +02002476 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002477 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002478
2479 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2480
2481 /* move last client and endpoint(s) to the empty space */
2482 --server_opts.ch_client_count;
2483 if (i < server_opts.ch_client_count) {
2484 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002485 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002486 } else if (!server_opts.ch_client_count) {
2487 free(server_opts.ch_clients);
2488 server_opts.ch_clients = NULL;
2489 }
2490
2491 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002492 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002493 }
2494 }
2495 }
2496
2497 /* WRITE UNLOCK */
2498 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2499
2500 return ret;
2501}
2502
2503API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002504nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002505{
2506 uint16_t i;
2507 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002508 struct nc_ch_endpt *endpt;
2509 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002510
2511 if (!client_name) {
2512 ERRARG("client_name");
2513 return -1;
2514 } else if (!endpt_name) {
2515 ERRARG("endpt_name");
2516 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002517 } else if (!ti) {
2518 ERRARG("ti");
2519 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002520 }
2521
2522 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002523 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002524 if (!client) {
2525 return -1;
2526 }
2527
2528 for (i = 0; i < client->ch_endpt_count; ++i) {
2529 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2530 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002531 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002532 }
2533 }
2534
2535 ++client->ch_endpt_count;
2536 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2537 if (!client->ch_endpts) {
2538 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002539 goto cleanup;
2540 }
2541 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2542
2543 memset(endpt, 0, sizeof *client->ch_endpts);
2544 endpt->name = lydict_insert(server_opts.ctx, endpt_name, 0);
2545 endpt->ti = ti;
2546 endpt->sock_pending = -1;
2547 endpt->ka.idle_time = 1;
2548 endpt->ka.max_probes = 10;
2549 endpt->ka.probe_interval = 5;
2550
2551 switch (ti) {
2552#ifdef NC_ENABLED_SSH
2553 case NC_TI_LIBSSH:
2554 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2555 if (!endpt->opts.ssh) {
2556 ERRMEM;
2557 goto cleanup;
2558 }
2559 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2560 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002561 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002562 break;
2563#endif
2564#ifdef NC_ENABLED_TLS
2565 case NC_TI_OPENSSL:
2566 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2567 if (!endpt->opts.tls) {
2568 ERRMEM;
2569 goto cleanup;
2570 }
2571 break;
2572#endif
2573 default:
2574 ERRINT;
2575 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002576 }
2577
Michal Vaskoadf30f02019-06-24 09:34:47 +02002578 /* success */
2579 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002580
Michal Vaskoadf30f02019-06-24 09:34:47 +02002581cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002582 /* UNLOCK */
2583 nc_server_ch_client_unlock(client);
2584
Michal Vaskoadf30f02019-06-24 09:34:47 +02002585 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002586}
2587
2588API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002589nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002590{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002591 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002592 struct nc_ch_client *client;
2593
2594 if (!client_name) {
2595 ERRARG("client_name");
2596 return -1;
2597 }
2598
2599 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002600 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002601 if (!client) {
2602 return -1;
2603 }
2604
Michal Vaskoadf30f02019-06-24 09:34:47 +02002605 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002606
2607 /* UNLOCK */
2608 nc_server_ch_client_unlock(client);
2609
2610 return ret;
2611}
2612
2613API int
2614nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2615{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002616 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002617 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002618
2619 if (!client_name) {
2620 ERRARG("client_name");
2621 return -1;
2622 } else if (!endpt_name) {
2623 ERRARG("endpt_name");
2624 return -1;
2625 } else if (!address) {
2626 ERRARG("address");
2627 return -1;
2628 }
2629
2630 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002631 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2632 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002633 return -1;
2634 }
2635
Michal Vaskoadf30f02019-06-24 09:34:47 +02002636 lydict_remove(server_opts.ctx, endpt->address);
2637 endpt->address = lydict_insert(server_opts.ctx, address, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002638
2639 /* UNLOCK */
2640 nc_server_ch_client_unlock(client);
2641
Michal Vaskoadf30f02019-06-24 09:34:47 +02002642 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002643}
2644
2645API int
2646nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2647{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002648 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002649 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002650
2651 if (!client_name) {
2652 ERRARG("client_name");
2653 return -1;
2654 } else if (!endpt_name) {
2655 ERRARG("endpt_name");
2656 return -1;
2657 } else if (!port) {
2658 ERRARG("port");
2659 return -1;
2660 }
2661
2662 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002663 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2664 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002665 return -1;
2666 }
2667
Michal Vaskoadf30f02019-06-24 09:34:47 +02002668 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002669
2670 /* UNLOCK */
2671 nc_server_ch_client_unlock(client);
2672
Michal Vaskoadf30f02019-06-24 09:34:47 +02002673 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002674}
2675
2676API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002677nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
2678{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002679 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002680 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002681
2682 if (!client_name) {
2683 ERRARG("client_name");
2684 return -1;
2685 } else if (!endpt_name) {
2686 ERRARG("endpt_name");
2687 return -1;
2688 }
2689
2690 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002691 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2692 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002693 return -1;
2694 }
2695
Michal Vaskoadf30f02019-06-24 09:34:47 +02002696 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002697
2698 /* UNLOCK */
2699 nc_server_ch_client_unlock(client);
2700
Michal Vaskoadf30f02019-06-24 09:34:47 +02002701 return -1;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002702}
2703
2704API int
2705nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
2706 int probe_interval)
2707{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002708 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002709 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002710
2711 if (!client_name) {
2712 ERRARG("client_name");
2713 return -1;
2714 } else if (!endpt_name) {
2715 ERRARG("endpt_name");
2716 return -1;
2717 }
2718
2719 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002720 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2721 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002722 return -1;
2723 }
2724
Michal Vaskoadf30f02019-06-24 09:34:47 +02002725 if (idle_time > -1) {
2726 endpt->ka.idle_time = idle_time;
2727 }
2728 if (max_probes > -1) {
2729 endpt->ka.max_probes = max_probes;
2730 }
2731 if (probe_interval > -1) {
2732 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002733 }
2734
2735 /* UNLOCK */
2736 nc_server_ch_client_unlock(client);
2737
Michal Vaskoadf30f02019-06-24 09:34:47 +02002738 return -1;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002739}
2740
2741API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002742nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2743{
2744 struct nc_ch_client *client;
2745
2746 if (!client_name) {
2747 ERRARG("client_name");
2748 return -1;
2749 } else if (!conn_type) {
2750 ERRARG("conn_type");
2751 return -1;
2752 }
2753
2754 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002755 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002756 if (!client) {
2757 return -1;
2758 }
2759
2760 if (client->conn_type != conn_type) {
2761 client->conn_type = conn_type;
2762
2763 /* set default options */
2764 switch (conn_type) {
2765 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002766 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002767 break;
2768 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002769 client->conn.period.period = 60;
2770 client->conn.period.anchor_time = 0;
2771 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002772 break;
2773 default:
2774 ERRINT;
2775 break;
2776 }
2777 }
2778
2779 /* UNLOCK */
2780 nc_server_ch_client_unlock(client);
2781
2782 return 0;
2783}
2784
2785API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002786nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
2787{
2788 struct nc_ch_client *client;
2789
2790 if (!client_name) {
2791 ERRARG("client_name");
2792 return -1;
2793 } else if (!period) {
2794 ERRARG("period");
2795 return -1;
2796 }
2797
2798 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002799 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002800 if (!client) {
2801 return -1;
2802 }
2803
2804 if (client->conn_type != NC_CH_PERIOD) {
2805 ERR("Call Home client \"%s\" is not of periodic connection type.");
2806 /* UNLOCK */
2807 nc_server_ch_client_unlock(client);
2808 return -1;
2809 }
2810
2811 client->conn.period.period = period;
2812
2813 /* UNLOCK */
2814 nc_server_ch_client_unlock(client);
2815
2816 return 0;
2817}
2818
2819API int
2820nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002821{
2822 struct nc_ch_client *client;
2823
2824 if (!client_name) {
2825 ERRARG("client_name");
2826 return -1;
2827 }
2828
2829 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002830 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002831 if (!client) {
2832 return -1;
2833 }
2834
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002835 if (client->conn_type != NC_CH_PERIOD) {
2836 ERR("Call Home client \"%s\" is not of periodic connection type.");
Michal Vasko2e6defd2016-10-07 15:48:15 +02002837 /* UNLOCK */
2838 nc_server_ch_client_unlock(client);
2839 return -1;
2840 }
2841
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002842 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002843
2844 /* UNLOCK */
2845 nc_server_ch_client_unlock(client);
2846
2847 return 0;
2848}
2849
2850API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002851nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002852{
2853 struct nc_ch_client *client;
2854
2855 if (!client_name) {
2856 ERRARG("client_name");
2857 return -1;
2858 }
2859
2860 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002861 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002862 if (!client) {
2863 return -1;
2864 }
2865
2866 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002867 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002868 /* UNLOCK */
2869 nc_server_ch_client_unlock(client);
2870 return -1;
2871 }
2872
2873 client->conn.period.idle_timeout = idle_timeout;
2874
2875 /* UNLOCK */
2876 nc_server_ch_client_unlock(client);
2877
2878 return 0;
2879}
2880
2881API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002882nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2883{
2884 struct nc_ch_client *client;
2885
2886 if (!client_name) {
2887 ERRARG("client_name");
2888 return -1;
2889 }
2890
2891 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002892 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002893 if (!client) {
2894 return -1;
2895 }
2896
2897 client->start_with = start_with;
2898
2899 /* UNLOCK */
2900 nc_server_ch_client_unlock(client);
2901
2902 return 0;
2903}
2904
2905API int
2906nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2907{
2908 struct nc_ch_client *client;
2909
2910 if (!client_name) {
2911 ERRARG("client_name");
2912 return -1;
2913 } else if (!max_attempts) {
2914 ERRARG("max_attempts");
2915 return -1;
2916 }
2917
2918 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002919 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002920 if (!client) {
2921 return -1;
2922 }
2923
2924 client->max_attempts = max_attempts;
2925
2926 /* UNLOCK */
2927 nc_server_ch_client_unlock(client);
2928
2929 return 0;
2930}
2931
2932/* client lock is expected to be held */
2933static NC_MSG_TYPE
Michal Vaskoadf30f02019-06-24 09:34:47 +02002934nc_connect_ch_endpt(struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01002935{
Michal Vasko71090fc2016-05-24 16:37:28 +02002936 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002937 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002938 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01002939 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01002940
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002941 sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->ka, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002942 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002943 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002944 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00002945 /* no need to store the socket as pending any longer */
2946 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01002947
Michal Vasko131120a2018-05-29 15:44:02 +02002948 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002949 if (!(*session)) {
2950 ERRMEM;
2951 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01002952 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002953 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002954 }
2955 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01002956 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002957 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko66032bc2019-01-22 15:03:12 +01002958 (*session)->host = lydict_insert_zc(server_opts.ctx, ip_host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002959 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002960
Michal Vaskob05053d2016-01-22 16:12:06 +01002961 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002962#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02002963 if (endpt->ti == NC_TI_LIBSSH) {
2964 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002965 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002966 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002967
Michal Vasko71090fc2016-05-24 16:37:28 +02002968 if (ret < 0) {
2969 msgtype = NC_MSG_ERROR;
2970 goto fail;
2971 } else if (!ret) {
2972 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002973 goto fail;
2974 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002975 } else
2976#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002977#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02002978 if (endpt->ti == NC_TI_OPENSSL) {
2979 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002980 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002981 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002982
Michal Vasko71090fc2016-05-24 16:37:28 +02002983 if (ret < 0) {
2984 msgtype = NC_MSG_ERROR;
2985 goto fail;
2986 } else if (!ret) {
2987 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002988 goto fail;
2989 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002990 } else
2991#endif
2992 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002993 ERRINT;
2994 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002995 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002996 goto fail;
2997 }
2998
2999 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01003000 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003001
3002 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003003 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003004 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003005 goto fail;
3006 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003007
3008 nc_gettimespec_mono(&ts_cur);
3009 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
3010 nc_gettimespec_real(&ts_cur);
3011 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003012 (*session)->status = NC_STATUS_RUNNING;
3013
Michal Vasko71090fc2016-05-24 16:37:28 +02003014 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003015
3016fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003017 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003018 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02003019 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003020}
3021
Michal Vasko2e6defd2016-10-07 15:48:15 +02003022struct nc_ch_client_thread_arg {
3023 char *client_name;
3024 void (*session_clb)(const char *client_name, struct nc_session *new_session);
3025};
3026
3027static struct nc_ch_client *
3028nc_server_ch_client_with_endpt_lock(const char *name)
3029{
3030 struct nc_ch_client *client;
3031
3032 while (1) {
3033 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003034 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003035 if (!client) {
3036 return NULL;
3037 }
3038 if (client->ch_endpt_count) {
3039 return client;
3040 }
3041 /* no endpoints defined yet */
3042
3043 /* UNLOCK */
3044 nc_server_ch_client_unlock(client);
3045
3046 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3047 }
3048
3049 return NULL;
3050}
3051
3052static int
3053nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3054{
Michal Vasko3f05a092018-03-13 10:39:49 +01003055 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003056 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003057 struct timespec ts;
3058 struct nc_ch_client *client;
3059
3060 /* session created, initialize condition */
Michal Vasko27377422018-03-15 08:59:35 +01003061 session->opts.server.ch_lock = calloc(1, sizeof *session->opts.server.ch_lock);
3062 session->opts.server.ch_cond = calloc(1, sizeof *session->opts.server.ch_cond);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003063 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
3064 ERRMEM;
3065 nc_session_free(session, NULL);
3066 return -1;
3067 }
3068 pthread_mutex_init(session->opts.server.ch_lock, NULL);
3069 pthread_cond_init(session->opts.server.ch_cond, NULL);
3070
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003071 session->flags |= NC_SESSION_CALLHOME;
3072
Michal Vasko2e6defd2016-10-07 15:48:15 +02003073 /* CH LOCK */
3074 pthread_mutex_lock(session->opts.server.ch_lock);
3075
3076 /* give the session to the user */
3077 data->session_clb(data->client_name, session);
3078
3079 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003080 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003081 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003082
Michal Vasko3f05a092018-03-13 10:39:49 +01003083 r = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
3084 if (!r) {
3085 /* we were woken up, something probably happened */
3086 if (session->status != NC_STATUS_RUNNING) {
3087 break;
3088 }
3089 } else if (r != ETIMEDOUT) {
3090 ERR("Pthread condition timedwait failed (%s).", strerror(r));
3091 ret = -1;
3092 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003093 }
3094
Michal Vasko2e6defd2016-10-07 15:48:15 +02003095 /* check whether the client was not removed */
3096 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003097 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003098 if (!client) {
3099 /* client was removed, finish thread */
3100 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003101 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003102 ret = 1;
3103 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003104 }
3105
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003106 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003107 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003108 } else {
3109 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003110 }
3111
Michal Vasko9fb42272017-10-05 13:50:05 +02003112 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003113 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 +02003114 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
3115 session->status = NC_STATUS_INVALID;
3116 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3117 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003118
3119 /* UNLOCK */
3120 nc_server_ch_client_unlock(client);
3121
3122 } while (session->status == NC_STATUS_RUNNING);
3123
Michal Vasko27377422018-03-15 08:59:35 +01003124 /* CH UNLOCK */
3125 pthread_mutex_unlock(session->opts.server.ch_lock);
3126
Michal Vasko3f05a092018-03-13 10:39:49 +01003127 if (session->status == NC_STATUS_CLOSING) {
3128 /* signal to nc_session_free() that we registered session being freed, otherwise it matters not */
3129 session->flags &= ~NC_SESSION_CALLHOME;
3130 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003131
Michal Vasko3f05a092018-03-13 10:39:49 +01003132 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003133}
3134
3135static void *
3136nc_ch_client_thread(void *arg)
3137{
3138 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3139 NC_MSG_TYPE msgtype;
3140 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003141 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003142 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003143 struct nc_ch_endpt *cur_endpt;
3144 struct nc_session *session;
3145 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003146 uint32_t client_id;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003147 time_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003148
3149 /* LOCK */
3150 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3151 if (!client) {
3152 goto cleanup;
3153 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003154 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003155
3156 cur_endpt = &client->ch_endpts[0];
3157 cur_endpt_name = strdup(cur_endpt->name);
3158
Michal Vasko29af44b2016-10-13 10:59:55 +02003159 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003160 while (1) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02003161 msgtype = nc_connect_ch_endpt(cur_endpt, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003162
3163 if (msgtype == NC_MSG_HELLO) {
3164 /* UNLOCK */
3165 nc_server_ch_client_unlock(client);
3166
Michal Vasko29af44b2016-10-13 10:59:55 +02003167 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003168 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3169 goto cleanup;
3170 }
Michal Vasko29af44b2016-10-13 10:59:55 +02003171 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003172
3173 /* LOCK */
3174 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3175 if (!client) {
3176 goto cleanup;
3177 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003178 if (client->id != client_id) {
3179 nc_server_ch_client_unlock(client);
3180 goto cleanup;
3181 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003182
3183 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003184 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003185 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003186 /* UNLOCK */
3187 nc_server_ch_client_unlock(client);
3188
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003189 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
3190 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
3191 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003192
3193 /* LOCK */
3194 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3195 if (!client) {
3196 goto cleanup;
3197 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003198 if (client->id != client_id) {
3199 nc_server_ch_client_unlock(client);
3200 goto cleanup;
3201 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003202 }
3203
3204 /* set next endpoint to try */
3205 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003206 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003207 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003208 /* we keep the current one but due to unlock/lock we have to find it again */
3209 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3210 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3211 break;
3212 }
3213 }
3214 if (next_endpt_index >= client->ch_endpt_count) {
3215 /* endpoint was removed, start with the first one */
3216 next_endpt_index = 0;
3217 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003218 } else {
3219 /* just get a random index */
3220 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003221 }
3222
Michal Vasko2e6defd2016-10-07 15:48:15 +02003223 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003224 /* UNLOCK */
3225 nc_server_ch_client_unlock(client);
3226
Michal Vasko2e6defd2016-10-07 15:48:15 +02003227 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003228 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3229
Michal Vasko6bb116b2016-10-26 13:53:46 +02003230 /* LOCK */
3231 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3232 if (!client) {
3233 goto cleanup;
3234 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003235 if (client->id != client_id) {
3236 nc_server_ch_client_unlock(client);
3237 goto cleanup;
3238 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003239
Michal Vasko2e6defd2016-10-07 15:48:15 +02003240 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003241
3242 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003243 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3244 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003245 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003246 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003247 }
3248
Peter Feiged05f2252018-09-03 08:09:47 +00003249 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003250 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003251 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003252 cur_attempts = 0;
3253 } else if (cur_attempts == client->max_attempts) {
3254 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003255 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003256 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003257 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003258 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003259 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003260 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003261 }
3262
3263 cur_attempts = 0;
3264 } /* else we keep the current one */
3265 }
Peter Feiged05f2252018-09-03 08:09:47 +00003266
3267 cur_endpt = &client->ch_endpts[next_endpt_index];
3268 free(cur_endpt_name);
3269 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003270 }
3271
3272cleanup:
3273 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003274 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003275 free(data->client_name);
3276 free(data);
3277 return NULL;
3278}
3279
3280API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02003281nc_connect_ch_client_dispatch(const char *client_name, void (*session_clb)(const char *client_name,
3282 struct nc_session *new_session))
Michal Vasko3f05a092018-03-13 10:39:49 +01003283{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003284 int ret;
3285 pthread_t tid;
3286 struct nc_ch_client_thread_arg *arg;
3287
3288 if (!client_name) {
3289 ERRARG("client_name");
3290 return -1;
3291 } else if (!session_clb) {
3292 ERRARG("session_clb");
3293 return -1;
3294 }
3295
3296 arg = malloc(sizeof *arg);
3297 if (!arg) {
3298 ERRMEM;
3299 return -1;
3300 }
3301 arg->client_name = strdup(client_name);
3302 if (!arg->client_name) {
3303 ERRMEM;
3304 free(arg);
3305 return -1;
3306 }
3307 arg->session_clb = session_clb;
3308
3309 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3310 if (ret) {
3311 ERR("Creating a new thread failed (%s).", strerror(ret));
3312 free(arg->client_name);
3313 free(arg);
3314 return -1;
3315 }
3316 /* the thread now manages arg */
3317
3318 pthread_detach(tid);
3319
3320 return 0;
3321}
3322
Radek Krejci53691be2016-02-22 13:58:37 +01003323#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003324
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003325API time_t
3326nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003327{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003328 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003329 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003330 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003331 }
3332
Michal Vasko2e6defd2016-10-07 15:48:15 +02003333 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003334}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003335
3336API void
3337nc_session_set_notif_status(struct nc_session *session, int notif_status)
3338{
3339 if (!session || (session->side != NC_SERVER)) {
3340 ERRARG("session");
3341 return;
3342 }
3343
3344 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3345}
3346
3347API int
3348nc_session_get_notif_status(const struct nc_session *session)
3349{
3350 if (!session || (session->side != NC_SERVER)) {
3351 ERRARG("session");
3352 return 0;
3353 }
3354
3355 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003356}
Michal Vasko8f430592019-02-26 08:32:54 +01003357
3358API int
3359nc_session_is_callhome(const struct nc_session *session)
3360{
3361 if (!session || (session->side != NC_SERVER)) {
3362 ERRARG("session");
3363 return 0;
3364 }
3365
3366 if (session->flags & NC_SESSION_CALLHOME) {
3367 return 1;
3368 }
3369
3370 return 0;
3371}