blob: 8caaf13b7ceca8d0ca27abed5ee13d04daf1ffd7 [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"
36
Michal Vaskob48aa812016-01-18 14:13:09 +010037struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010038#ifdef NC_ENABLED_SSH
39 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
40#endif
41 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020042 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
43 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010044};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010045
fanchanghu966f2de2016-07-21 02:28:57 -040046static nc_rpc_clb global_rpc_clb = NULL;
47
Michal Vasko3031aae2016-01-27 16:07:18 +010048struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010049nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010050{
51 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010052 struct nc_endpt *endpt = NULL;
53
Michal Vaskoade892d2017-02-22 13:40:35 +010054 /* WRITE LOCK */
55 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010056
57 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020058 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010059 endpt = &server_opts.endpts[i];
60 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010061 }
62 }
63
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010064 if (!endpt) {
65 ERR("Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010066 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020067 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010068 return NULL;
69 }
70
Michal Vaskoe2713da2016-08-22 16:06:40 +020071 if (idx) {
72 *idx = i;
73 }
74
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010075 return endpt;
76}
77
Michal Vasko2e6defd2016-10-07 15:48:15 +020078struct nc_ch_client *
79nc_server_ch_client_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010080{
Michal Vasko2e6defd2016-10-07 15:48:15 +020081 uint16_t i;
82 struct nc_ch_client *client = NULL;
83
84 /* READ LOCK */
85 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
86
87 for (i = 0; i < server_opts.ch_client_count; ++i) {
88 if (!strcmp(server_opts.ch_clients[i].name, name) && (!ti || (server_opts.ch_clients[i].ti == ti))) {
89 client = &server_opts.ch_clients[i];
90 break;
91 }
92 }
93
94 if (!client) {
95 ERR("Call Home client \"%s\" was not found.", name);
96 /* READ UNLOCK */
97 pthread_rwlock_unlock(&server_opts.ch_client_lock);
98 return NULL;
99 }
100
101 /* CH CLIENT LOCK */
102 pthread_mutex_lock(&client->lock);
103
104 if (idx) {
105 *idx = i;
106 }
107
108 return client;
109}
110
111void
112nc_server_ch_client_unlock(struct nc_ch_client *client)
113{
114 /* CH CLIENT UNLOCK */
115 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100116
117 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200118 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100119}
Michal Vasko086311b2016-01-08 09:53:11 +0100120
Michal Vasko1a38c862016-01-15 15:50:07 +0100121API void
122nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
123{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200124 if (!session) {
125 ERRARG("session");
126 return;
127 } else if (!reason) {
128 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100129 return;
130 }
131
Michal Vasko142cfea2017-08-07 10:12:11 +0200132 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
133 session->killed_by = 0;
134 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100135 session->term_reason = reason;
136}
137
Michal Vasko142cfea2017-08-07 10:12:11 +0200138API void
139nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
140{
141 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
142 ERRARG("session");
143 return;
144 } else if (!sid) {
145 ERRARG("sid");
146 return;
147 }
148
149 session->killed_by = sid;
150}
151
152API void
153nc_session_set_status(struct nc_session *session, NC_STATUS status)
154{
155 if (!session) {
156 ERRARG("session");
157 return;
158 } else if (!status) {
159 ERRARG("status");
160 return;
161 }
162
163 session->status = status;
164}
165
Michal Vasko086311b2016-01-08 09:53:11 +0100166int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200167nc_sock_listen_inet(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100168{
Michal Vasko06c860d2018-07-09 16:08:52 +0200169 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100170 int is_ipv4, sock;
171 struct sockaddr_storage saddr;
172
173 struct sockaddr_in *saddr4;
174 struct sockaddr_in6 *saddr6;
175
176
177 if (!strchr(address, ':')) {
178 is_ipv4 = 1;
179 } else {
180 is_ipv4 = 0;
181 }
182
183 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
184 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100185 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100186 goto fail;
187 }
188
Michal Vaskobe52dc22018-10-17 09:28:17 +0200189 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200190 opt = 1;
191 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
192 ERR("Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
193 goto fail;
194 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200195
196 if (nc_sock_enable_keepalive(sock)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100197 goto fail;
198 }
199
200 bzero(&saddr, sizeof(struct sockaddr_storage));
201 if (is_ipv4) {
202 saddr4 = (struct sockaddr_in *)&saddr;
203
204 saddr4->sin_family = AF_INET;
205 saddr4->sin_port = htons(port);
206
207 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100208 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100209 goto fail;
210 }
211
212 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100213 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100214 goto fail;
215 }
216
217 } else {
218 saddr6 = (struct sockaddr_in6 *)&saddr;
219
220 saddr6->sin6_family = AF_INET6;
221 saddr6->sin6_port = htons(port);
222
223 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100224 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100225 goto fail;
226 }
227
228 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100229 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100230 goto fail;
231 }
232 }
233
Michal Vaskofb89d772016-01-08 12:25:35 +0100234 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100235 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100236 goto fail;
237 }
238
239 return sock;
240
241fail:
242 if (sock > -1) {
243 close(sock);
244 }
245
246 return -1;
247}
248
249int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200250nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
251{
252 struct sockaddr_un sun;
253 int sock = -1;
254
255 sock = socket(AF_UNIX, SOCK_STREAM, 0);
256 if (sock == -1) {
257 ERR("Failed to create socket (%s).", strerror(errno));
258 goto fail;
259 }
260
261 memset(&sun, 0, sizeof(sun));
262 sun.sun_family = AF_UNIX;
263 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", address);
264
265 unlink(sun.sun_path);
266 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
267 ERR("Could not bind \"%s\" (%s).", address, strerror(errno));
268 goto fail;
269 }
270
271 if (opts->mode != (mode_t)-1) {
272 if (chmod(sun.sun_path, opts->mode) < 0) {
273 ERR("Failed to set unix socket permissions (%s).", strerror(errno));
274 goto fail;
275 }
276 }
277
278 if (opts->uid != (uid_t)-1 || opts->gid != (gid_t)-1) {
279 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
280 ERR("Failed to set unix socket uid/gid (%s).", strerror(errno));
281 goto fail;
282 }
283 }
284
285 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
286 ERR("Unable to start listening on \"%s\" (%s).", address, strerror(errno));
287 goto fail;
288 }
289
290 return sock;
291
292fail:
293 if (sock > -1) {
294 close(sock);
295 }
296
297 return -1;
298}
299
300int
Michal Vasko3031aae2016-01-27 16:07:18 +0100301nc_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 +0100302{
Michal Vaskof54cd352017-02-22 13:42:02 +0100303 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100304 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100305 struct pollfd *pfd;
306 struct sockaddr_storage saddr;
307 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100308 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100309
310 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100311 if (!pfd) {
312 ERRMEM;
313 return -1;
314 }
315
Michal Vaskoac2f6182017-01-30 14:32:03 +0100316 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200317 if (binds[i].sock < 0) {
318 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200319 continue;
320 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200321 if (binds[i].pollin) {
322 binds[i].pollin = 0;
323 /* leftover pollin */
324 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100325 break;
326 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100327 pfd[pfd_count].fd = binds[i].sock;
328 pfd[pfd_count].events = POLLIN;
329 pfd[pfd_count].revents = 0;
330
331 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100332 }
333
Michal Vasko0a3f3752016-10-13 14:58:38 +0200334 if (sock == -1) {
335 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100336 sigfillset(&sigmask);
337 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100338 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100339 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
340
Michal Vasko0a3f3752016-10-13 14:58:38 +0200341 if (!ret) {
342 /* we timeouted */
343 free(pfd);
344 return 0;
345 } else if (ret == -1) {
346 ERR("Poll failed (%s).", strerror(errno));
347 free(pfd);
348 return -1;
349 }
Michal Vasko086311b2016-01-08 09:53:11 +0100350
Michal Vaskoac2f6182017-01-30 14:32:03 +0100351 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
352 /* adjust i so that indices in binds and pfd always match */
353 while (binds[i].sock != pfd[j].fd) {
354 ++i;
355 }
356
357 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200358 --ret;
359
360 if (!ret) {
361 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100362 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200363 break;
364 } else {
365 /* just remember the event for next time */
366 binds[i].pollin = 1;
367 }
368 }
Michal Vasko086311b2016-01-08 09:53:11 +0100369 }
370 }
371 free(pfd);
372
373 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100374 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100375 return -1;
376 }
377
378 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100379 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100380 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100381 return -1;
382 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200383 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100384
Michal Vasko0190bc32016-03-02 15:47:49 +0100385 /* make the socket non-blocking */
386 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
387 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100388 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100389 return -1;
390 }
391
Michal Vasko3031aae2016-01-27 16:07:18 +0100392 if (idx) {
393 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100394 }
395
Michal Vasko086311b2016-01-08 09:53:11 +0100396 /* host was requested */
397 if (host) {
398 if (saddr.ss_family == AF_INET) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000399 *host = malloc(INET_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100400 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000401 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100402 ERR("inet_ntop failed (%s).", strerror(errno));
403 free(*host);
404 *host = NULL;
405 }
Michal Vasko086311b2016-01-08 09:53:11 +0100406
Michal Vasko4eb3c312016-03-01 14:09:37 +0100407 if (port) {
408 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
409 }
410 } else {
411 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100412 }
413 } else if (saddr.ss_family == AF_INET6) {
Jan Kundrát0f942e82018-02-14 14:52:00 +0100414 *host = malloc(INET6_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100415 if (*host) {
Frank Rimpler740c22f2018-08-06 13:55:16 +0000416 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100417 ERR("inet_ntop failed (%s).", strerror(errno));
418 free(*host);
419 *host = NULL;
420 }
Michal Vasko086311b2016-01-08 09:53:11 +0100421
Michal Vasko4eb3c312016-03-01 14:09:37 +0100422 if (port) {
423 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
424 }
425 } else {
426 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100427 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200428 } else if (saddr.ss_family == AF_UNIX) {
429 *host = strdup(((struct sockaddr_un *)&saddr)->sun_path);
430 if (*host) {
431 if (port) {
432 *port = 0;
433 }
434 } else {
435 ERRMEM;
436 }
Michal Vasko086311b2016-01-08 09:53:11 +0100437 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100438 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100439 }
440 }
441
442 return ret;
443}
444
Michal Vasko05ba9df2016-01-13 14:40:27 +0100445static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100446nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100447{
448 const char *identifier = NULL, *version = NULL, *format = NULL;
449 char *model_data = NULL;
450 const struct lys_module *module;
451 struct nc_server_error *err;
452 struct lyd_node *child, *data = NULL;
Michal Vasko88639e92017-08-03 14:38:10 +0200453 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100454
455 LY_TREE_FOR(rpc->child, child) {
456 if (!strcmp(child->schema->name, "identifier")) {
457 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
458 } else if (!strcmp(child->schema->name, "version")) {
459 version = ((struct lyd_node_leaf_list *)child)->value_str;
Radek Krejci1afa7792017-03-26 11:24:16 -0500460 if (version && version[0] == '\0') {
461 version = NULL;
462 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100463 } else if (!strcmp(child->schema->name, "format")) {
464 format = ((struct lyd_node_leaf_list *)child)->value_str;
465 }
466 }
467
468 /* check version */
469 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100470 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
471 nc_err_set_msg(err, "The requested version is not supported.", "en");
472 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100473 }
474
475 /* check and get module with the name identifier */
Radek Krejci3222b7d2017-09-21 16:04:30 +0200476 module = ly_ctx_get_module(server_opts.ctx, identifier, version, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100477 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200478 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
479 }
480 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100481 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
482 nc_err_set_msg(err, "The requested schema was not found.", "en");
483 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100484 }
485
486 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100487 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100488 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL, 0, 0);
Radek Krejci90fba642016-12-07 15:59:45 +0100489 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100490 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100491 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100492 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
493 nc_err_set_msg(err, "The requested format is not supported.", "en");
494 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100495 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200496 if (!model_data) {
497 ERRINT;
498 return NULL;
499 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100500
Michal Vasko88639e92017-08-03 14:38:10 +0200501 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/data", 1);
502 if (!sdata) {
503 ERRINT;
504 free(model_data);
505 return NULL;
506 }
507
Radek Krejci539efb62016-08-24 15:05:16 +0200508 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
509 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100510 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100511 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200512 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100513 return NULL;
514 }
515
Radek Krejci36dfdb32016-09-01 16:56:35 +0200516 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100517}
518
519static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100520nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100521{
Michal Vasko428087d2016-01-14 16:04:28 +0100522 session->term_reason = NC_SESSION_TERM_CLOSED;
523 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100524}
525
Michal Vasko086311b2016-01-08 09:53:11 +0100526API int
527nc_server_init(struct ly_ctx *ctx)
528{
Michal Vasko88639e92017-08-03 14:38:10 +0200529 const struct lys_node *rpc;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000530 pthread_rwlockattr_t attr;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100531
Michal Vasko086311b2016-01-08 09:53:11 +0100532 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200533 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100534 return -1;
535 }
536
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100537 nc_init();
538
Michal Vasko05ba9df2016-01-13 14:40:27 +0100539 /* set default <get-schema> callback if not specified */
Michal Vasko88639e92017-08-03 14:38:10 +0200540 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
541 if (rpc && !rpc->priv) {
542 lys_set_private(rpc, nc_clb_default_get_schema);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100543 }
544
545 /* set default <close-session> callback if not specififed */
Michal Vasko88639e92017-08-03 14:38:10 +0200546 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session", 0);
547 if (rpc && !rpc->priv) {
548 lys_set_private(rpc, nc_clb_default_close_session);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100549 }
550
Michal Vasko086311b2016-01-08 09:53:11 +0100551 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100552
553 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500554 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100555
Frank Rimpler9f838b02018-07-25 06:44:03 +0000556 errno=0;
557
558 if (pthread_rwlockattr_init(&attr) == 0) {
559 if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
560 if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
561 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
562 }
563 if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
564 ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
565 }
566 } else {
567 ERR("%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
568 }
569 pthread_rwlockattr_destroy(&attr);
570 } else {
571 ERR("%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
572 }
Michal Vasko086311b2016-01-08 09:53:11 +0100573 return 0;
574}
575
Michal Vaskob48aa812016-01-18 14:13:09 +0100576API void
577nc_server_destroy(void)
578{
Radek Krejci658782b2016-12-04 22:04:55 +0100579 unsigned int i;
580
581 for (i = 0; i < server_opts.capabilities_count; i++) {
582 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
583 }
584 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200585 server_opts.capabilities = NULL;
586 server_opts.capabilities_count = 0;
587
Radek Krejci53691be2016-02-22 13:58:37 +0100588#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100589 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100590#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100591#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100592 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
593 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
594 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200595 server_opts.passwd_auth_data = NULL;
596 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100597
Michal Vasko17dfda92016-12-01 14:06:16 +0100598 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100599
600 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
601 server_opts.hostkey_data_free(server_opts.hostkey_data);
602 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200603 server_opts.hostkey_data = NULL;
604 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100605#endif
606#ifdef NC_ENABLED_TLS
607 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
608 server_opts.server_cert_data_free(server_opts.server_cert_data);
609 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200610 server_opts.server_cert_data = NULL;
611 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100612 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
613 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
614 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200615 server_opts.trusted_cert_list_data = NULL;
616 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100617#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100618 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100619}
620
Michal Vasko086311b2016-01-08 09:53:11 +0100621API int
622nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
623{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200624 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
625 ERRARG("basic_mode");
626 return -1;
627 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
628 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100629 return -1;
630 }
631
632 server_opts.wd_basic_mode = basic_mode;
633 server_opts.wd_also_supported = also_supported;
634 return 0;
635}
636
Michal Vasko1a38c862016-01-15 15:50:07 +0100637API void
Michal Vasko55f03972016-04-13 08:56:01 +0200638nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
639{
640 if (!basic_mode && !also_supported) {
641 ERRARG("basic_mode and also_supported");
642 return;
643 }
644
645 if (basic_mode) {
646 *basic_mode = server_opts.wd_basic_mode;
647 }
648 if (also_supported) {
649 *also_supported = server_opts.wd_also_supported;
650 }
651}
652
Michal Vasko55f03972016-04-13 08:56:01 +0200653API int
Radek Krejci658782b2016-12-04 22:04:55 +0100654nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200655{
Radek Krejci658782b2016-12-04 22:04:55 +0100656 const char **new;
657
658 if (!value || !value[0]) {
659 ERRARG("value must not be empty");
660 return EXIT_FAILURE;
661 }
662
663 server_opts.capabilities_count++;
664 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
665 if (!new) {
666 ERRMEM;
667 return EXIT_FAILURE;
668 }
669 server_opts.capabilities = new;
670 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
671
672 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200673}
674
Michal Vasko1a38c862016-01-15 15:50:07 +0100675API void
Michal Vasko086311b2016-01-08 09:53:11 +0100676nc_server_set_hello_timeout(uint16_t hello_timeout)
677{
Michal Vasko086311b2016-01-08 09:53:11 +0100678 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100679}
680
Michal Vasko55f03972016-04-13 08:56:01 +0200681API uint16_t
682nc_server_get_hello_timeout(void)
683{
684 return server_opts.hello_timeout;
685}
686
Michal Vasko1a38c862016-01-15 15:50:07 +0100687API void
Michal Vasko086311b2016-01-08 09:53:11 +0100688nc_server_set_idle_timeout(uint16_t idle_timeout)
689{
Michal Vasko086311b2016-01-08 09:53:11 +0100690 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100691}
692
Michal Vasko55f03972016-04-13 08:56:01 +0200693API uint16_t
694nc_server_get_idle_timeout(void)
695{
696 return server_opts.idle_timeout;
697}
698
Michal Vasko71090fc2016-05-24 16:37:28 +0200699API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100700nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100701{
Michal Vasko71090fc2016-05-24 16:37:28 +0200702 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200703 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200704
Michal Vasko45e53ae2016-04-07 11:46:03 +0200705 if (!server_opts.ctx) {
706 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200707 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200708 } else if (fdin < 0) {
709 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200710 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200711 } else if (fdout < 0) {
712 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200713 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200714 } else if (!username) {
715 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200716 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200717 } else if (!session) {
718 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200719 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100720 }
721
722 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200723 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100724 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100725 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200726 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100727 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100728 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100729
Michal Vasko086311b2016-01-08 09:53:11 +0100730 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100731 (*session)->ti_type = NC_TI_FD;
732 (*session)->ti.fd.in = fdin;
733 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100734
735 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100736 (*session)->flags = NC_SESSION_SHAREDCTX;
737 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100738
Michal Vaskob48aa812016-01-18 14:13:09 +0100739 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +0100740 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100741
Michal Vasko086311b2016-01-08 09:53:11 +0100742 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200743 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200744 if (msgtype != NC_MSG_HELLO) {
745 nc_session_free(*session, NULL);
746 *session = NULL;
747 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100748 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200749
750 nc_gettimespec_mono(&ts_cur);
751 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
752 nc_gettimespec_real(&ts_cur);
753 (*session)->opts.server.session_start = ts_cur.tv_sec;
754
Michal Vasko1a38c862016-01-15 15:50:07 +0100755 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100756
Michal Vasko71090fc2016-05-24 16:37:28 +0200757 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100758}
Michal Vasko9e036d52016-01-08 10:49:26 +0100759
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200760static int
761nc_accept_unix(struct nc_session *session, int sock)
762{
763 const struct passwd *pw;
764 struct ucred ucred;
765 char *username;
766 socklen_t len;
767
768 session->ti_type = NC_TI_UNIX;
769
770 len = sizeof(ucred);
771 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
772 ERR("Failed to get credentials from unix socket (%s).",
773 strerror(errno));
774 close(sock);
775 return -1;
776 }
777
778 pw = getpwuid(ucred.uid);
779 if (pw == NULL) {
780 ERR("Failed to find username for uid=%u (%s).\n", ucred.uid,
781 strerror(errno));
782 close(sock);
783 return -1;
784 }
785
786 username = strdup(pw->pw_name);
787 if (username == NULL) {
788 ERRMEM;
789 close(sock);
790 return -1;
791 }
792 session->username = lydict_insert_zc(server_opts.ctx, username);
793
794 session->ti.unixsock.sock = sock;
795
796 return 1;
797}
798
Michal Vaskob30b99c2016-07-26 11:35:43 +0200799static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100800nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
801{
802 uint8_t q_last;
803
804 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
805 ERRINT;
806 return;
807 }
808
809 /* get a unique queue value (by adding 1 to the last added value, if any) */
810 if (ps->queue_len) {
811 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
812 *id = ps->queue[q_last] + 1;
813 } else {
814 *id = 0;
815 }
816
817 /* add the id into the queue */
818 ++ps->queue_len;
819 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
820 ps->queue[q_last] = *id;
821}
822
823static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200824nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
825{
Michal Vasko74c345f2018-02-07 10:37:11 +0100826 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200827
828 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100829 /* get the actual queue idx */
830 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200831
832 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100833 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200834 /* another equal value, simply cannot be */
835 ERRINT;
836 }
Michal Vaskod8340032018-02-12 14:41:00 +0100837 if (found == 2) {
838 /* move the following values */
839 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
840 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100841 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200842 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100843 if (i == 0) {
844 found = 1;
845 } else {
846 /* this is not okay, our id is in the middle of the queue */
847 found = 2;
848 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200849 }
850 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200851 if (!found) {
852 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100853 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200854 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100855
Michal Vasko103fe632018-02-12 16:37:45 +0100856 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100857 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100858 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100859 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
860 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200861}
862
Michal Vaskof04a52a2016-04-07 10:52:10 +0200863int
Michal Vasko26043172016-07-26 14:08:59 +0200864nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200865{
866 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200867 struct timespec ts;
868
Michal Vasko77a6abe2017-10-05 10:02:20 +0200869 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100870 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200871
872 /* LOCK */
873 ret = pthread_mutex_timedlock(&ps->lock, &ts);
874 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200875 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200876 return -1;
877 }
878
Michal Vasko74c345f2018-02-07 10:37:11 +0100879 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +0100880 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100881 ERR("%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +0100882 pthread_mutex_unlock(&ps->lock);
883 return -1;
884 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100885
886 /* add ourselves into the queue */
887 nc_ps_queue_add_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200888
889 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200890 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200891 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +0100892 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200893
894 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
895 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +0530896 /**
897 * This may happen when another thread releases the lock and broadcasts the condition
898 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
899 * but when actually this thread was ready for condition.
900 */
preetbhansali629dfc42018-12-17 16:04:40 +0530901 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +0530902 break;
903 }
Michal Vasko66032bc2019-01-22 15:03:12 +0100904
Michal Vasko26043172016-07-26 14:08:59 +0200905 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200906 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200907 nc_ps_queue_remove_id(ps, *id);
908 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200909 return -1;
910 }
911 }
912
Michal Vaskobe86fe32016-04-07 10:43:03 +0200913 /* UNLOCK */
914 pthread_mutex_unlock(&ps->lock);
915
916 return 0;
917}
918
Michal Vaskof04a52a2016-04-07 10:52:10 +0200919int
Michal Vasko26043172016-07-26 14:08:59 +0200920nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200921{
922 int ret;
923 struct timespec ts;
924
Michal Vasko77a6abe2017-10-05 10:02:20 +0200925 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100926 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200927
928 /* LOCK */
929 ret = pthread_mutex_timedlock(&ps->lock, &ts);
930 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200931 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200932 ret = -1;
933 }
934
Michal Vaskob30b99c2016-07-26 11:35:43 +0200935 /* we must be the first, it was our turn after all, right? */
936 if (ps->queue[ps->queue_begin] != id) {
937 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200938 /* UNLOCK */
939 if (!ret) {
940 pthread_mutex_unlock(&ps->lock);
941 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200942 return -1;
943 }
944
Michal Vaskobe86fe32016-04-07 10:43:03 +0200945 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200946 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200947
948 /* broadcast to all other threads that the queue moved */
949 pthread_cond_broadcast(&ps->cond);
950
Michal Vaskobe86fe32016-04-07 10:43:03 +0200951 /* UNLOCK */
952 if (!ret) {
953 pthread_mutex_unlock(&ps->lock);
954 }
955
956 return ret;
957}
958
Michal Vasko428087d2016-01-14 16:04:28 +0100959API struct nc_pollsession *
960nc_ps_new(void)
961{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100962 struct nc_pollsession *ps;
963
964 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100965 if (!ps) {
966 ERRMEM;
967 return NULL;
968 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200969 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100970 pthread_mutex_init(&ps->lock, NULL);
971
972 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100973}
974
975API void
976nc_ps_free(struct nc_pollsession *ps)
977{
fanchanghu3d4e7212017-08-09 09:42:30 +0800978 uint16_t i;
979
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100980 if (!ps) {
981 return;
982 }
983
Michal Vaskobe86fe32016-04-07 10:43:03 +0200984 if (ps->queue_len) {
985 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
986 }
987
fanchanghu3d4e7212017-08-09 09:42:30 +0800988 for (i = 0; i < ps->session_count; i++) {
989 free(ps->sessions[i]);
990 }
991
Michal Vasko428087d2016-01-14 16:04:28 +0100992 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100993 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200994 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100995
Michal Vasko428087d2016-01-14 16:04:28 +0100996 free(ps);
997}
998
999API int
1000nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1001{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001002 uint8_t q_id;
1003
Michal Vasko45e53ae2016-04-07 11:46:03 +02001004 if (!ps) {
1005 ERRARG("ps");
1006 return -1;
1007 } else if (!session) {
1008 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001009 return -1;
1010 }
1011
Michal Vasko48a63ed2016-03-01 09:48:21 +01001012 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001013 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001014 return -1;
1015 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001016
Michal Vasko428087d2016-01-14 16:04:28 +01001017 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001018 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001019 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001020 ERRMEM;
1021 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001022 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001023 return -1;
1024 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001025 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1026 if (!ps->sessions[ps->session_count - 1]) {
1027 ERRMEM;
1028 --ps->session_count;
1029 /* UNLOCK */
1030 nc_ps_unlock(ps, q_id, __func__);
1031 return -1;
1032 }
1033 ps->sessions[ps->session_count - 1]->session = session;
1034 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001035
Michal Vasko48a63ed2016-03-01 09:48:21 +01001036 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001037 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001038}
1039
Michal Vasko48a63ed2016-03-01 09:48:21 +01001040static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001041_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001042{
1043 uint16_t i;
1044
Radek Krejcid5f978f2016-03-03 13:14:45 +01001045 if (index >= 0) {
1046 i = (uint16_t)index;
1047 goto remove;
1048 }
Michal Vasko428087d2016-01-14 16:04:28 +01001049 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001050 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001051remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001052 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001053 if (i <= ps->session_count) {
1054 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001055 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001056 }
1057 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001058 free(ps->sessions);
1059 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001060 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001061 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001062 return 0;
1063 }
1064 }
1065
Michal Vaskof0537d82016-01-29 14:42:38 +01001066 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001067}
1068
Michal Vasko48a63ed2016-03-01 09:48:21 +01001069API int
1070nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1071{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001072 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001073 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001074
Michal Vasko45e53ae2016-04-07 11:46:03 +02001075 if (!ps) {
1076 ERRARG("ps");
1077 return -1;
1078 } else if (!session) {
1079 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001080 return -1;
1081 }
1082
1083 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001084 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001085 return -1;
1086 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001087
Radek Krejcid5f978f2016-03-03 13:14:45 +01001088 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001089
1090 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001091 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001092
Michal Vaskobe86fe32016-04-07 10:43:03 +02001093 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001094}
1095
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001096API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001097nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001098{
1099 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001100 struct nc_session *ret = NULL;
1101
1102 if (!ps) {
1103 ERRARG("ps");
1104 return NULL;
1105 }
1106
1107 /* LOCK */
1108 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1109 return NULL;
1110 }
1111
Michal Vasko4871c9d2017-10-09 14:48:39 +02001112 if (idx < ps->session_count) {
1113 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001114 }
1115
1116 /* UNLOCK */
1117 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1118
1119 return ret;
1120}
1121
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001122API uint16_t
1123nc_ps_session_count(struct nc_pollsession *ps)
1124{
1125 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001126 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001127 return 0;
1128 }
1129
Michal Vaskof4462fd2017-02-15 14:29:05 +01001130 return ps->session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001131}
1132
Michal Vasko131120a2018-05-29 15:44:02 +02001133/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001134 * returns: NC_PSPOLL_ERROR,
1135 * NC_PSPOLL_BAD_RPC,
1136 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1137 * NC_PSPOLL_RPC
1138 */
1139static int
Michal Vasko131120a2018-05-29 15:44:02 +02001140nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001141{
1142 struct lyxml_elem *xml = NULL;
1143 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001144 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001145 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001146
Michal Vasko45e53ae2016-04-07 11:46:03 +02001147 if (!session) {
1148 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001149 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001150 } else if (!rpc) {
1151 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001152 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001153 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001154 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001155 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001156 }
1157
Michal Vasko131120a2018-05-29 15:44:02 +02001158 msgtype = nc_read_msg_io(session, io_timeout, &xml, 0);
Michal Vasko428087d2016-01-14 16:04:28 +01001159
1160 switch (msgtype) {
1161 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001162 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001163 if (!*rpc) {
1164 ERRMEM;
1165 goto error;
1166 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001167
Radek Krejcif93c7d42016-04-06 13:41:15 +02001168 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001169 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1170 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001171 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001172 /* parsing RPC failed */
Michal Vaskoc9970242018-02-14 16:03:35 +01001173 reply = nc_server_reply_err(nc_err_libyang(server_opts.ctx));
Michal Vasko131120a2018-05-29 15:44:02 +02001174 ret = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001175 nc_server_reply_free(reply);
1176 if (ret == -1) {
1177 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001178 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001179 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1180 } else {
1181 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001182 }
Michal Vasko428087d2016-01-14 16:04:28 +01001183 (*rpc)->root = xml;
1184 break;
1185 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001186 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001187 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001188 goto error;
1189 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001190 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001191 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001192 goto error;
1193 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001194 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001195 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001196 goto error;
1197 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001198 /* NC_MSG_ERROR,
Michal Vasko131120a2018-05-29 15:44:02 +02001199 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg_io()
Michal Vasko428087d2016-01-14 16:04:28 +01001200 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001201 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001202 break;
1203 }
1204
Michal Vasko71090fc2016-05-24 16:37:28 +02001205 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001206
1207error:
1208 /* cleanup */
1209 lyxml_free(server_opts.ctx, xml);
1210
Michal Vasko71090fc2016-05-24 16:37:28 +02001211 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001212}
1213
fanchanghu966f2de2016-07-21 02:28:57 -04001214API void
1215nc_set_global_rpc_clb(nc_rpc_clb clb)
1216{
1217 global_rpc_clb = clb;
1218}
1219
Radek Krejci93e80222016-10-03 13:34:25 +02001220API NC_MSG_TYPE
1221nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1222{
Michal Vasko131120a2018-05-29 15:44:02 +02001223 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001224
1225 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001226 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001227 ERRARG("session");
1228 return NC_MSG_ERROR;
1229 } else if (!notif || !notif->tree || !notif->eventtime) {
1230 ERRARG("notif");
1231 return NC_MSG_ERROR;
1232 }
1233
Michal Vasko131120a2018-05-29 15:44:02 +02001234 /* we do not need RPC lock for this, IO lock will be acquired properly */
1235 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
1236 if (ret == NC_MSG_ERROR) {
Radek Krejci93e80222016-10-03 13:34:25 +02001237 ERR("Session %u: failed to write notification.", session->id);
Radek Krejci93e80222016-10-03 13:34:25 +02001238 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001239
Michal Vasko131120a2018-05-29 15:44:02 +02001240 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001241}
1242
Michal Vasko131120a2018-05-29 15:44:02 +02001243/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001244 * returns: NC_PSPOLL_ERROR,
1245 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1246 * NC_PSPOLL_REPLY_ERROR,
1247 * 0
1248 */
1249static int
Michal Vasko131120a2018-05-29 15:44:02 +02001250nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001251{
1252 nc_rpc_clb clb;
1253 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001254 struct lys_node *rpc_act = NULL;
1255 struct lyd_node *next, *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001256 int ret = 0;
1257 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001258
Michal Vasko4a827e52016-03-03 10:59:00 +01001259 if (!rpc) {
1260 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001261 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001262 }
1263
Michal Vasko90e8e692016-07-13 12:27:57 +02001264 if (rpc->tree->schema->nodetype == LYS_RPC) {
1265 /* RPC */
1266 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001267 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001268 /* action */
1269 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1270 if (elem->schema->nodetype == LYS_ACTION) {
1271 rpc_act = elem->schema;
1272 break;
1273 }
1274 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001275 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001276 if (!rpc_act) {
1277 ERRINT;
1278 return NC_PSPOLL_ERROR;
1279 }
1280 }
1281
1282 if (!rpc_act->priv) {
1283 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001284 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001285 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001286 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001287 reply = clb(rpc->tree, session);
1288 }
1289
1290 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001291 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001292 }
Michal Vasko131120a2018-05-29 15:44:02 +02001293 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->root, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001294 if (reply->type == NC_RPL_ERROR) {
1295 ret |= NC_PSPOLL_REPLY_ERROR;
1296 }
1297 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001298
Michal Vasko131120a2018-05-29 15:44:02 +02001299 if (r != NC_MSG_REPLY) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001300 ERR("Session %u: failed to write reply.", session->id);
1301 ret |= NC_PSPOLL_ERROR;
1302 }
Michal Vasko428087d2016-01-14 16:04:28 +01001303
1304 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1305 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1306 session->status = NC_STATUS_INVALID;
1307 }
1308
Michal Vasko71090fc2016-05-24 16:37:28 +02001309 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001310}
1311
Michal Vasko131120a2018-05-29 15:44:02 +02001312/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001313 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1314 * NC_PSPOLL_ERROR, (msg filled)
1315 * NC_PSPOLL_TIMEOUT,
1316 * NC_PSPOLL_RPC (some application data available),
1317 * NC_PSPOLL_SSH_CHANNEL,
1318 * NC_PSPOLL_SSH_MSG
1319 */
1320static int
Michal Vasko131120a2018-05-29 15:44:02 +02001321nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001322{
Michal Vasko9a327362017-01-11 11:31:46 +01001323 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001324 int r, ret = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001325#ifdef NC_ENABLED_SSH
1326 struct nc_session *new;
1327#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001328
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001329 /* check timeout first */
1330 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001331 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001332 sprintf(msg, "session idle timeout elapsed");
1333 session->status = NC_STATUS_INVALID;
1334 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1335 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1336 }
1337
Michal Vasko131120a2018-05-29 15:44:02 +02001338 r = nc_session_io_lock(session, io_timeout, __func__);
1339 if (r < 0) {
1340 sprintf(msg, "session IO lock failed to be acquired");
1341 return NC_PSPOLL_ERROR;
1342 } else if (!r) {
1343 return NC_PSPOLL_TIMEOUT;
1344 }
1345
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001346 switch (session->ti_type) {
1347#ifdef NC_ENABLED_SSH
1348 case NC_TI_LIBSSH:
1349 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001350 if (r == SSH_EOF) {
1351 sprintf(msg, "SSH channel unexpected EOF");
1352 session->status = NC_STATUS_INVALID;
1353 session->term_reason = NC_SESSION_TERM_DROPPED;
1354 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1355 } else if (r == SSH_ERROR) {
1356 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001357 session->status = NC_STATUS_INVALID;
1358 session->term_reason = NC_SESSION_TERM_OTHER;
1359 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001360 } else if (!r) {
1361 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1362 /* new SSH message */
1363 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1364 if (session->ti.libssh.next) {
1365 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1366 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1367 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1368 /* new NETCONF SSH channel */
1369 ret = NC_PSPOLL_SSH_CHANNEL;
1370 break;
1371 }
1372 }
1373 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001374 break;
1375 }
1376 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001377
Michal Vasko8dcaa882017-10-19 14:28:42 +02001378 /* just some SSH message */
1379 ret = NC_PSPOLL_SSH_MSG;
1380 } else {
1381 ret = NC_PSPOLL_TIMEOUT;
1382 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001383 } else {
1384 /* we have some application data */
1385 ret = NC_PSPOLL_RPC;
1386 }
1387 break;
1388#endif
1389#ifdef NC_ENABLED_TLS
1390 case NC_TI_OPENSSL:
1391 r = SSL_pending(session->ti.tls);
1392 if (!r) {
1393 /* no data pending in the SSL buffer, poll fd */
1394 pfd.fd = SSL_get_rfd(session->ti.tls);
1395 if (pfd.fd < 0) {
1396 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1397 ret = NC_PSPOLL_ERROR;
1398 break;
1399 }
1400 pfd.events = POLLIN;
1401 pfd.revents = 0;
1402 r = poll(&pfd, 1, 0);
1403
1404 if ((r < 0) && (errno != EINTR)) {
1405 sprintf(msg, "poll failed (%s)", strerror(errno));
1406 session->status = NC_STATUS_INVALID;
1407 ret = NC_PSPOLL_ERROR;
1408 } else if (r > 0) {
1409 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1410 sprintf(msg, "communication socket unexpectedly closed");
1411 session->status = NC_STATUS_INVALID;
1412 session->term_reason = NC_SESSION_TERM_DROPPED;
1413 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1414 } else if (pfd.revents & POLLERR) {
1415 sprintf(msg, "communication socket error");
1416 session->status = NC_STATUS_INVALID;
1417 session->term_reason = NC_SESSION_TERM_OTHER;
1418 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1419 } else {
1420 ret = NC_PSPOLL_RPC;
1421 }
1422 } else {
1423 ret = NC_PSPOLL_TIMEOUT;
1424 }
1425 } else {
1426 ret = NC_PSPOLL_RPC;
1427 }
1428 break;
1429#endif
1430 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001431 case NC_TI_UNIX:
1432 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001433 pfd.events = POLLIN;
1434 pfd.revents = 0;
1435 r = poll(&pfd, 1, 0);
1436
1437 if ((r < 0) && (errno != EINTR)) {
1438 sprintf(msg, "poll failed (%s)", strerror(errno));
1439 session->status = NC_STATUS_INVALID;
1440 ret = NC_PSPOLL_ERROR;
1441 } else if (r > 0) {
1442 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1443 sprintf(msg, "communication socket unexpectedly closed");
1444 session->status = NC_STATUS_INVALID;
1445 session->term_reason = NC_SESSION_TERM_DROPPED;
1446 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1447 } else if (pfd.revents & POLLERR) {
1448 sprintf(msg, "communication socket error");
1449 session->status = NC_STATUS_INVALID;
1450 session->term_reason = NC_SESSION_TERM_OTHER;
1451 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1452 } else {
1453 ret = NC_PSPOLL_RPC;
1454 }
1455 } else {
1456 ret = NC_PSPOLL_TIMEOUT;
1457 }
1458 break;
1459 case NC_TI_NONE:
1460 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1461 ret = NC_PSPOLL_ERROR;
1462 break;
1463 }
1464
Michal Vasko131120a2018-05-29 15:44:02 +02001465 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001466 return ret;
1467}
1468
1469API int
1470nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1471{
1472 int ret, r;
1473 uint8_t q_id;
1474 uint16_t i, j;
1475 char msg[256];
1476 struct timespec ts_timeout, ts_cur;
1477 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001478 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001479 struct nc_server_rpc *rpc = NULL;
1480
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001481 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001482 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001483 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001484 }
1485
Michal Vaskoade892d2017-02-22 13:40:35 +01001486 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001487 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001488 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001489 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001490
Michal Vaskoade892d2017-02-22 13:40:35 +01001491 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001492 nc_ps_unlock(ps, q_id, __func__);
1493 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001494 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001495
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001496 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001497 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001498 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001499 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001500 nc_addtimespec(&ts_timeout, timeout);
1501 }
1502
1503 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001504 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001505 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001506 if (ps->last_event_session == ps->session_count - 1) {
1507 i = j = 0;
1508 } else {
1509 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001510 }
Michal Vasko9a327362017-01-11 11:31:46 +01001511 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001512 cur_ps_session = ps->sessions[i];
1513 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001514
Michal Vasko131120a2018-05-29 15:44:02 +02001515 /* SESSION RPC LOCK */
1516 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001517 if (r == -1) {
1518 ret = NC_PSPOLL_ERROR;
1519 } else if (r == 1) {
1520 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001521 switch (cur_ps_session->state) {
1522 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001523 if (cur_session->status == NC_STATUS_RUNNING) {
1524 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001525 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001526
Michal Vasko131120a2018-05-29 15:44:02 +02001527 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001528 switch (ret) {
1529 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1530 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001531 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001532 break;
1533 case NC_PSPOLL_ERROR:
1534 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001535 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001536 break;
1537 case NC_PSPOLL_TIMEOUT:
1538#ifdef NC_ENABLED_SSH
1539 case NC_PSPOLL_SSH_CHANNEL:
1540 case NC_PSPOLL_SSH_MSG:
1541#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001542 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001543 break;
1544 case NC_PSPOLL_RPC:
1545 /* let's keep the state busy, we are not done with this session */
1546 break;
1547 }
1548 } else {
1549 /* session is not fine, let the caller know */
1550 ret = NC_PSPOLL_SESSION_TERM;
1551 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1552 ret |= NC_PSPOLL_SESSION_ERROR;
1553 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001554 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001555 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001556 break;
1557 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001558 /* it definitely should not be busy because we have the lock */
1559 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001560 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001561 break;
1562 case NC_PS_STATE_INVALID:
1563 /* we got it locked, but it will be freed, let it be */
1564 ret = NC_PSPOLL_TIMEOUT;
1565 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001566 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001567
Michal Vasko131120a2018-05-29 15:44:02 +02001568 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001569 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001570 /* SESSION RPC UNLOCK */
1571 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001572 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001573 } else {
1574 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001575 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001576 }
Michal Vasko428087d2016-01-14 16:04:28 +01001577
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001578 /* something happened */
1579 if (ret != NC_PSPOLL_TIMEOUT) {
1580 break;
1581 }
1582
Michal Vasko9a327362017-01-11 11:31:46 +01001583 if (i == ps->session_count - 1) {
1584 i = 0;
1585 } else {
1586 ++i;
1587 }
1588 } while (i != j);
1589
Michal Vaskoade892d2017-02-22 13:40:35 +01001590 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001591 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001592 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001593 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001594 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001595
1596 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1597 /* final timeout */
1598 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001599 }
Michal Vasko428087d2016-01-14 16:04:28 +01001600 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001601 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001602
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001603 /* do we want to return the session? */
1604 switch (ret) {
1605 case NC_PSPOLL_RPC:
1606 case NC_PSPOLL_SESSION_TERM:
1607 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1608#ifdef NC_ENABLED_SSH
1609 case NC_PSPOLL_SSH_CHANNEL:
1610 case NC_PSPOLL_SSH_MSG:
1611#endif
1612 if (session) {
1613 *session = cur_session;
1614 }
1615 ps->last_event_session = i;
1616 break;
1617 default:
1618 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001619 }
Michal Vasko428087d2016-01-14 16:04:28 +01001620
Michal Vaskoade892d2017-02-22 13:40:35 +01001621 /* PS UNLOCK */
1622 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001623
Michal Vasko131120a2018-05-29 15:44:02 +02001624 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001625 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001626 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001627 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1628 if (cur_session->status != NC_STATUS_RUNNING) {
1629 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001630 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001631 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001632 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001633 }
1634 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001635 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001636
Michal Vasko7f1ee932018-10-11 09:41:42 +02001637 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001638 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001639 if (cur_session->status != NC_STATUS_RUNNING) {
1640 ret |= NC_PSPOLL_SESSION_TERM;
1641 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1642 ret |= NC_PSPOLL_SESSION_ERROR;
1643 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001644 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001645 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001646 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001647 }
Michal Vasko428087d2016-01-14 16:04:28 +01001648 }
Michal Vasko7f1ee932018-10-11 09:41:42 +02001649 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001650
Michal Vasko131120a2018-05-29 15:44:02 +02001651 /* SESSION RPC UNLOCK */
1652 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001653 }
1654
Michal Vasko48a63ed2016-03-01 09:48:21 +01001655 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001656}
1657
Michal Vaskod09eae62016-02-01 10:32:52 +01001658API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001659nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001660{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001661 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001662 uint16_t i;
1663 struct nc_session *session;
1664
Michal Vasko9a25e932016-02-01 10:36:42 +01001665 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001666 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001667 return;
1668 }
1669
Michal Vasko48a63ed2016-03-01 09:48:21 +01001670 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001671 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001672 return;
1673 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001674
Michal Vasko48a63ed2016-03-01 09:48:21 +01001675 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001676 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001677 nc_session_free(ps->sessions[i]->session, data_free);
1678 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001679 }
1680 free(ps->sessions);
1681 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001682 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001683 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001684 } else {
1685 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001686 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1687 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001688 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001689 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001690 continue;
1691 }
1692
1693 ++i;
1694 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001695 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001696
1697 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001698 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001699}
1700
Radek Krejci53691be2016-02-22 13:58:37 +01001701#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001702
Michal Vaskoe2713da2016-08-22 16:06:40 +02001703API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001704nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001705{
Michal Vasko3031aae2016-01-27 16:07:18 +01001706 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001707 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001708
Michal Vasko45e53ae2016-04-07 11:46:03 +02001709 if (!name) {
1710 ERRARG("name");
1711 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001712 }
1713
Michal Vaskoade892d2017-02-22 13:40:35 +01001714 /* BIND LOCK */
1715 pthread_mutex_lock(&server_opts.bind_lock);
1716
1717 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001718 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001719
1720 /* check name uniqueness */
1721 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001722 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001723 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001724 ret = -1;
1725 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001726 }
1727 }
1728
Michal Vasko3031aae2016-01-27 16:07:18 +01001729 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001730 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001731 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001732 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001733 ret = -1;
1734 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001735 }
1736 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001737 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001738
Michal Vaskoe2713da2016-08-22 16:06:40 +02001739 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001740 if (!server_opts.binds) {
1741 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001742 ret = -1;
1743 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001744 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001745
Michal Vasko2e6defd2016-10-07 15:48:15 +02001746 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1747 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1748 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001749 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001750
1751 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001752#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001753 case NC_TI_LIBSSH:
1754 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1755 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1756 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001757 ret = -1;
1758 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001759 }
1760 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1761 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1762 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1763 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1764 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001765#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001766#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001767 case NC_TI_OPENSSL:
1768 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1769 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1770 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001771 ret = -1;
1772 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001773 }
1774 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001775#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001776 case NC_TI_UNIX:
1777 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1778 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1779 ERRMEM;
1780 ret = -1;
1781 goto cleanup;
1782 }
1783 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1784 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1785 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1786 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001787 default:
1788 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001789 ret = -1;
1790 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001791 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001792
Michal Vaskoade892d2017-02-22 13:40:35 +01001793cleanup:
1794 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001795 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001796
Michal Vaskoade892d2017-02-22 13:40:35 +01001797 /* BIND UNLOCK */
1798 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001799
Michal Vaskoade892d2017-02-22 13:40:35 +01001800 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001801}
1802
Michal Vasko3031aae2016-01-27 16:07:18 +01001803int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001804nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001805{
1806 struct nc_endpt *endpt;
1807 struct nc_bind *bind = NULL;
1808 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001809 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001810
Michal Vasko45e53ae2016-04-07 11:46:03 +02001811 if (!endpt_name) {
1812 ERRARG("endpt_name");
1813 return -1;
1814 } else if ((!address && !port) || (address && port)) {
1815 ERRARG("address and port");
1816 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001817 }
1818
Michal Vaskoe2713da2016-08-22 16:06:40 +02001819 if (address) {
1820 set_addr = 1;
1821 } else {
1822 set_addr = 0;
1823 }
1824
Michal Vaskoade892d2017-02-22 13:40:35 +01001825 /* BIND LOCK */
1826 pthread_mutex_lock(&server_opts.bind_lock);
1827
1828 /* ENDPT LOCK */
1829 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001830 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001831 /* BIND UNLOCK */
1832 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001833 return -1;
1834 }
1835
Michal Vaskoe2713da2016-08-22 16:06:40 +02001836 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001837
Michal Vaskoe2713da2016-08-22 16:06:40 +02001838 if (set_addr) {
1839 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001840 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001841 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001842 }
1843
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001844 if (!set_addr && endpt->ti == NC_TI_UNIX) {
1845 ret = -1;
1846 goto cleanup;
1847 }
1848
Michal Vaskoe2713da2016-08-22 16:06:40 +02001849 /* we have all the information we need to create a listening socket */
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001850 if (address && (port || endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001851 /* create new socket, close the old one */
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001852 if (endpt->ti == NC_TI_UNIX)
1853 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
1854 else
1855 sock = nc_sock_listen_inet(address, port);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001856 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001857 ret = -1;
1858 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001859 }
1860
1861 if (bind->sock > -1) {
1862 close(bind->sock);
1863 }
1864 bind->sock = sock;
1865 } /* else we are just setting address or port */
1866
1867 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001868 lydict_remove(server_opts.ctx, bind->address);
1869 bind->address = lydict_insert(server_opts.ctx, address, 0);
1870 } else {
1871 bind->port = port;
1872 }
1873
Michal Vaskoe2713da2016-08-22 16:06:40 +02001874 if (sock > -1) {
1875#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001876 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001877#elif defined(NC_ENABLED_SSH)
1878 VRB("Listening on %s:%u for SSH connections.", address, port);
1879#else
1880 VRB("Listening on %s:%u for TLS connections.", address, port);
1881#endif
1882 }
1883
Michal Vasko4c1fb492017-01-30 14:31:07 +01001884cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001885 /* ENDPT UNLOCK */
1886 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001887
Michal Vaskoade892d2017-02-22 13:40:35 +01001888 /* BIND UNLOCK */
1889 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001890
Michal Vasko4c1fb492017-01-30 14:31:07 +01001891 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001892}
1893
Michal Vaskoe2713da2016-08-22 16:06:40 +02001894API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001895nc_server_endpt_set_address(const char *endpt_name, const char *address)
1896{
1897 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1898}
1899
1900API int
1901nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1902{
1903 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1904}
1905
1906API int
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001907nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
1908{
1909 struct nc_endpt *endpt;
1910 uint16_t i;
1911 int ret = 0;
1912
1913 if (!endpt_name) {
1914 ERRARG("endpt_name");
1915 return -1;
1916 } else if (mode == 0) {
1917 ERRARG("mode");
1918 return -1;
1919 }
1920
1921 /* ENDPT LOCK */
1922 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
1923 if (!endpt)
1924 return -1;
1925
1926 if (endpt->ti != NC_TI_UNIX) {
1927 ret = -1;
1928 goto cleanup;
1929 }
1930
1931 endpt->opts.unixsock->mode = mode;
1932 endpt->opts.unixsock->uid = uid;
1933 endpt->opts.unixsock->gid = gid;
1934
1935cleanup:
1936 /* ENDPT UNLOCK */
1937 pthread_rwlock_unlock(&server_opts.endpt_lock);
1938
1939 return ret;
1940}
1941
1942API int
Michal Vasko59050372016-11-22 14:33:55 +01001943nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001944{
1945 uint32_t i;
1946 int ret = -1;
1947
Michal Vaskoade892d2017-02-22 13:40:35 +01001948 /* BIND LOCK */
1949 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001950
Michal Vaskoade892d2017-02-22 13:40:35 +01001951 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001952 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001953
Michal Vasko59050372016-11-22 14:33:55 +01001954 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001955 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001956 for (i = 0; i < server_opts.endpt_count; ++i) {
1957 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001958 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001959#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001960 case NC_TI_LIBSSH:
1961 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1962 free(server_opts.endpts[i].opts.ssh);
1963 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001964#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001965#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001966 case NC_TI_OPENSSL:
1967 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1968 free(server_opts.endpts[i].opts.tls);
1969 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001970#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001971 case NC_TI_UNIX:
1972 free(server_opts.endpts[i].opts.unixsock);
1973 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001974 default:
1975 ERRINT;
1976 /* won't get here ...*/
1977 break;
1978 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001979 ret = 0;
1980 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001981 free(server_opts.endpts);
1982 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001983
1984 /* remove all binds */
1985 for (i = 0; i < server_opts.endpt_count; ++i) {
1986 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1987 if (server_opts.binds[i].sock > -1) {
1988 close(server_opts.binds[i].sock);
1989 }
1990 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001991 free(server_opts.binds);
1992 server_opts.binds = NULL;
1993
Michal Vasko3031aae2016-01-27 16:07:18 +01001994 server_opts.endpt_count = 0;
1995
Michal Vasko1a38c862016-01-15 15:50:07 +01001996 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001997 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001998 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001999 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002000 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01002001 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002002 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002003#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002004 case NC_TI_LIBSSH:
2005 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2006 free(server_opts.endpts[i].opts.ssh);
2007 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002008#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002009#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002010 case NC_TI_OPENSSL:
2011 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2012 free(server_opts.endpts[i].opts.tls);
2013 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002014#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002015 case NC_TI_UNIX:
2016 free(server_opts.endpts[i].opts.unixsock);
2017 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002018 default:
2019 ERRINT;
2020 break;
2021 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002022
Michal Vaskoe2713da2016-08-22 16:06:40 +02002023 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02002024 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2025 if (server_opts.binds[i].sock > -1) {
2026 close(server_opts.binds[i].sock);
2027 }
2028
2029 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002030 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002031 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002032 free(server_opts.binds);
2033 server_opts.binds = NULL;
2034 free(server_opts.endpts);
2035 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002036 } else if (i < server_opts.endpt_count) {
2037 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2038 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002039 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002040
2041 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002042 if (name) {
2043 break;
2044 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002045 }
2046 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002047 }
2048
Michal Vaskoade892d2017-02-22 13:40:35 +01002049 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002050 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002051
Michal Vaskoade892d2017-02-22 13:40:35 +01002052 /* BIND UNLOCK */
2053 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002054
2055 return ret;
2056}
2057
Michal Vasko71090fc2016-05-24 16:37:28 +02002058API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002059nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002060{
Michal Vasko71090fc2016-05-24 16:37:28 +02002061 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002062 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002063 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002064 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002065 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002066
Michal Vasko45e53ae2016-04-07 11:46:03 +02002067 if (!server_opts.ctx) {
2068 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002069 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002070 } else if (!session) {
2071 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002072 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002073 }
2074
Michal Vaskoade892d2017-02-22 13:40:35 +01002075 /* BIND LOCK */
2076 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002077
2078 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02002079 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002080 /* BIND UNLOCK */
2081 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002082 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002083 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002084
Michal Vaskoe2713da2016-08-22 16:06:40 +02002085 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002086 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002087 /* BIND UNLOCK */
2088 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002089 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002090 if (!ret) {
2091 return NC_MSG_WOULDBLOCK;
2092 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002093 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002094 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002095
2096 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2097 /* ENDPT READ LOCK */
2098 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2099
2100 /* BIND UNLOCK */
2101 pthread_mutex_unlock(&server_opts.bind_lock);
2102
Michal Vaskob48aa812016-01-18 14:13:09 +01002103 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002104
Michal Vasko131120a2018-05-29 15:44:02 +02002105 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002106 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002107 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002108 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002109 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002110 msgtype = NC_MSG_ERROR;
2111 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002112 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002113 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002114 (*session)->ctx = server_opts.ctx;
2115 (*session)->flags = NC_SESSION_SHAREDCTX;
2116 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
2117 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002118
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002119 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002120#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002121 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2122 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002123 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002124 if (ret < 0) {
2125 msgtype = NC_MSG_ERROR;
2126 goto cleanup;
2127 } else if (!ret) {
2128 msgtype = NC_MSG_WOULDBLOCK;
2129 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002130 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002131 } else
2132#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002133#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002134 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2135 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002136 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002137 if (ret < 0) {
2138 msgtype = NC_MSG_ERROR;
2139 goto cleanup;
2140 } else if (!ret) {
2141 msgtype = NC_MSG_WOULDBLOCK;
2142 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002143 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002144 } else
2145#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002146 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2147 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2148 ret = nc_accept_unix(*session, sock);
2149 if (ret < 0) {
2150 msgtype = NC_MSG_ERROR;
2151 goto cleanup;
2152 }
2153 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002154 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002155 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002156 msgtype = NC_MSG_ERROR;
2157 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002158 }
2159
Michal Vasko2cc4c682016-03-01 09:16:48 +01002160 (*session)->data = NULL;
2161
Michal Vaskoade892d2017-02-22 13:40:35 +01002162 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002163 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002164
Michal Vaskob48aa812016-01-18 14:13:09 +01002165 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002166 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002167
Michal Vasko9e036d52016-01-08 10:49:26 +01002168 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002169 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002170 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002171 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002172 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002173 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002174 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002175
2176 nc_gettimespec_mono(&ts_cur);
2177 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2178 nc_gettimespec_real(&ts_cur);
2179 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002180 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002181
Michal Vasko71090fc2016-05-24 16:37:28 +02002182 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002183
Michal Vasko71090fc2016-05-24 16:37:28 +02002184cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002185 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002186 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002187
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002188 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002189 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002190 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002191}
2192
Michal Vasko2e6defd2016-10-07 15:48:15 +02002193API int
2194nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
2195{
2196 uint16_t i;
2197
2198 if (!name) {
2199 ERRARG("name");
2200 return -1;
2201 } else if (!ti) {
2202 ERRARG("ti");
2203 return -1;
2204 }
2205
2206 /* WRITE LOCK */
2207 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2208
2209 /* check name uniqueness */
2210 for (i = 0; i < server_opts.ch_client_count; ++i) {
2211 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2212 ERR("Call Home client \"%s\" already exists.", name);
2213 /* WRITE UNLOCK */
2214 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2215 return -1;
2216 }
2217 }
2218
2219 ++server_opts.ch_client_count;
2220 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2221 if (!server_opts.ch_clients) {
2222 ERRMEM;
2223 /* WRITE UNLOCK */
2224 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2225 return -1;
2226 }
2227 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko69a3ff62018-11-09 09:31:48 +01002228 server_opts.ch_clients[server_opts.ch_client_count - 1].id = ATOMIC_INC(&server_opts.new_client_id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002229 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002230 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
2231 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002232
2233 switch (ti) {
2234#ifdef NC_ENABLED_SSH
2235 case NC_TI_LIBSSH:
2236 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2237 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
2238 ERRMEM;
2239 /* WRITE UNLOCK */
2240 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2241 return -1;
2242 }
2243 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
2244 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2245 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
2246 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
2247 break;
2248#endif
2249#ifdef NC_ENABLED_TLS
2250 case NC_TI_OPENSSL:
2251 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2252 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
2253 ERRMEM;
2254 /* WRITE UNLOCK */
2255 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2256 return -1;
2257 }
2258 break;
2259#endif
2260 default:
2261 ERRINT;
2262 /* WRITE UNLOCK */
2263 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2264 return -1;
2265 }
2266
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002267 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
2268
Michal Vasko2e6defd2016-10-07 15:48:15 +02002269 /* set CH default options */
2270 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
2271 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
2272
2273 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
2274
2275 /* WRITE UNLOCK */
2276 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2277
2278 return 0;
2279}
2280
2281API int
Michal Vasko59050372016-11-22 14:33:55 +01002282nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002283{
2284 uint16_t i, j;
2285 int ret = -1;
2286
2287 /* WRITE LOCK */
2288 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2289
Michal Vasko59050372016-11-22 14:33:55 +01002290 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002291 /* remove all CH clients */
2292 for (i = 0; i < server_opts.ch_client_count; ++i) {
2293 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2294
2295 /* remove all endpoints */
2296 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2297 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2298 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2299 }
2300 free(server_opts.ch_clients[i].ch_endpts);
2301
2302 switch (server_opts.ch_clients[i].ti) {
2303#ifdef NC_ENABLED_SSH
2304 case NC_TI_LIBSSH:
2305 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2306 free(server_opts.ch_clients[i].opts.ssh);
2307 break;
2308#endif
2309#ifdef NC_ENABLED_TLS
2310 case NC_TI_OPENSSL:
2311 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2312 free(server_opts.ch_clients[i].opts.tls);
2313 break;
2314#endif
2315 default:
2316 ERRINT;
2317 /* won't get here ...*/
2318 break;
2319 }
2320
2321 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2322
2323 ret = 0;
2324 }
2325 free(server_opts.ch_clients);
2326 server_opts.ch_clients = NULL;
2327
2328 server_opts.ch_client_count = 0;
2329
2330 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002331 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002332 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002333 if ((name && !strcmp(server_opts.ch_clients[i].name, name)) || (!name && (server_opts.ch_clients[i].ti == ti))) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002334 /* remove endpt */
2335 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2336
2337 switch (server_opts.ch_clients[i].ti) {
2338#ifdef NC_ENABLED_SSH
2339 case NC_TI_LIBSSH:
2340 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2341 free(server_opts.ch_clients[i].opts.ssh);
2342 break;
2343#endif
2344#ifdef NC_ENABLED_TLS
2345 case NC_TI_OPENSSL:
2346 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2347 free(server_opts.ch_clients[i].opts.tls);
2348 break;
2349#endif
2350 default:
2351 ERRINT;
2352 break;
2353 }
2354
2355 /* remove all endpoints */
2356 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2357 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2358 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2359 }
2360 free(server_opts.ch_clients[i].ch_endpts);
2361
2362 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2363
2364 /* move last client and endpoint(s) to the empty space */
2365 --server_opts.ch_client_count;
2366 if (i < server_opts.ch_client_count) {
2367 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2368 sizeof *server_opts.ch_clients);
2369 } else if (!server_opts.ch_client_count) {
2370 free(server_opts.ch_clients);
2371 server_opts.ch_clients = NULL;
2372 }
2373
2374 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002375 if (name) {
2376 break;
2377 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002378 }
2379 }
2380 }
2381
2382 /* WRITE UNLOCK */
2383 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2384
2385 return ret;
2386}
2387
2388API int
2389nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2390{
2391 uint16_t i;
2392 struct nc_ch_client *client;
2393
2394 if (!client_name) {
2395 ERRARG("client_name");
2396 return -1;
2397 } else if (!endpt_name) {
2398 ERRARG("endpt_name");
2399 return -1;
2400 }
2401
2402 /* LOCK */
2403 client = nc_server_ch_client_lock(client_name, 0, NULL);
2404 if (!client) {
2405 return -1;
2406 }
2407
2408 for (i = 0; i < client->ch_endpt_count; ++i) {
2409 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2410 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2411 /* UNLOCK */
2412 nc_server_ch_client_unlock(client);
2413 return -1;
2414 }
2415 }
2416
2417 ++client->ch_endpt_count;
2418 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2419 if (!client->ch_endpts) {
2420 ERRMEM;
2421 /* UNLOCK */
2422 nc_server_ch_client_unlock(client);
2423 return -1;
2424 }
2425
2426 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2427 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2428 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
Frank Rimpler9f838b02018-07-25 06:44:03 +00002429 client->ch_endpts[client->ch_endpt_count - 1].sock_pending = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002430
2431 /* UNLOCK */
2432 nc_server_ch_client_unlock(client);
2433
2434 return 0;
2435}
2436
2437API int
2438nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2439{
2440 uint16_t i;
2441 int ret = -1;
2442 struct nc_ch_client *client;
2443
2444 if (!client_name) {
2445 ERRARG("client_name");
2446 return -1;
2447 }
2448
2449 /* LOCK */
2450 client = nc_server_ch_client_lock(client_name, 0, NULL);
2451 if (!client) {
2452 return -1;
2453 }
2454
2455 if (!endpt_name) {
2456 /* remove all endpoints */
2457 for (i = 0; i < client->ch_endpt_count; ++i) {
2458 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2459 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Frank Rimpler9f838b02018-07-25 06:44:03 +00002460 if (client->ch_endpts[i].sock_pending != -1) {
2461 close(client->ch_endpts[i].sock_pending);
2462 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002463 }
2464 free(client->ch_endpts);
2465 client->ch_endpts = NULL;
2466 client->ch_endpt_count = 0;
2467
2468 ret = 0;
2469 } else {
2470 for (i = 0; i < client->ch_endpt_count; ++i) {
2471 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2472 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2473 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002474
Michal Vasko4f921012016-10-20 14:07:45 +02002475 /* move last endpoint to the empty space */
2476 --client->ch_endpt_count;
2477 if (i < client->ch_endpt_count) {
2478 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2479 } else if (!server_opts.ch_client_count) {
2480 free(server_opts.ch_clients);
2481 server_opts.ch_clients = NULL;
2482 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002483
Michal Vasko4f921012016-10-20 14:07:45 +02002484 ret = 0;
2485 break;
2486 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002487 }
2488 }
2489
2490 /* UNLOCK */
2491 nc_server_ch_client_unlock(client);
2492
2493 return ret;
2494}
2495
2496API int
2497nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2498{
2499 uint16_t i;
2500 int ret = -1;
2501 struct nc_ch_client *client;
2502
2503 if (!client_name) {
2504 ERRARG("client_name");
2505 return -1;
2506 } else if (!endpt_name) {
2507 ERRARG("endpt_name");
2508 return -1;
2509 } else if (!address) {
2510 ERRARG("address");
2511 return -1;
2512 }
2513
2514 /* LOCK */
2515 client = nc_server_ch_client_lock(client_name, 0, NULL);
2516 if (!client) {
2517 return -1;
2518 }
2519
2520 for (i = 0; i < client->ch_endpt_count; ++i) {
2521 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2522 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2523 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2524
2525 ret = 0;
2526 break;
2527 }
2528 }
2529
2530 /* UNLOCK */
2531 nc_server_ch_client_unlock(client);
2532
2533 if (ret == -1) {
2534 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2535 }
2536
2537 return ret;
2538}
2539
2540API int
2541nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2542{
2543 uint16_t i;
2544 int ret = -1;
2545 struct nc_ch_client *client;
2546
2547 if (!client_name) {
2548 ERRARG("client_name");
2549 return -1;
2550 } else if (!endpt_name) {
2551 ERRARG("endpt_name");
2552 return -1;
2553 } else if (!port) {
2554 ERRARG("port");
2555 return -1;
2556 }
2557
2558 /* LOCK */
2559 client = nc_server_ch_client_lock(client_name, 0, NULL);
2560 if (!client) {
2561 return -1;
2562 }
2563
2564 for (i = 0; i < client->ch_endpt_count; ++i) {
2565 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2566 client->ch_endpts[i].port = port;
2567
2568 ret = 0;
2569 break;
2570 }
2571 }
2572
2573 /* UNLOCK */
2574 nc_server_ch_client_unlock(client);
2575
2576 if (ret == -1) {
2577 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2578 }
2579
2580 return ret;
2581}
2582
2583API int
2584nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2585{
2586 struct nc_ch_client *client;
2587
2588 if (!client_name) {
2589 ERRARG("client_name");
2590 return -1;
2591 } else if (!conn_type) {
2592 ERRARG("conn_type");
2593 return -1;
2594 }
2595
2596 /* LOCK */
2597 client = nc_server_ch_client_lock(client_name, 0, NULL);
2598 if (!client) {
2599 return -1;
2600 }
2601
2602 if (client->conn_type != conn_type) {
2603 client->conn_type = conn_type;
2604
2605 /* set default options */
2606 switch (conn_type) {
2607 case NC_CH_PERSIST:
2608 client->conn.persist.idle_timeout = 86400;
2609 client->conn.persist.ka_max_wait = 30;
2610 client->conn.persist.ka_max_attempts = 3;
2611 break;
2612 case NC_CH_PERIOD:
2613 client->conn.period.idle_timeout = 300;
2614 client->conn.period.reconnect_timeout = 60;
2615 break;
2616 default:
2617 ERRINT;
2618 break;
2619 }
2620 }
2621
2622 /* UNLOCK */
2623 nc_server_ch_client_unlock(client);
2624
2625 return 0;
2626}
2627
2628API int
2629nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2630{
2631 struct nc_ch_client *client;
2632
2633 if (!client_name) {
2634 ERRARG("client_name");
2635 return -1;
2636 }
2637
2638 /* LOCK */
2639 client = nc_server_ch_client_lock(client_name, 0, NULL);
2640 if (!client) {
2641 return -1;
2642 }
2643
2644 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002645 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002646 /* UNLOCK */
2647 nc_server_ch_client_unlock(client);
2648 return -1;
2649 }
2650
2651 client->conn.persist.idle_timeout = idle_timeout;
2652
2653 /* UNLOCK */
2654 nc_server_ch_client_unlock(client);
2655
2656 return 0;
2657}
2658
2659API int
2660nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2661{
2662 struct nc_ch_client *client;
2663
2664 if (!client_name) {
2665 ERRARG("client_name");
2666 return -1;
2667 } else if (!max_wait) {
2668 ERRARG("max_wait");
2669 return -1;
2670 }
2671
2672 /* LOCK */
2673 client = nc_server_ch_client_lock(client_name, 0, NULL);
2674 if (!client) {
2675 return -1;
2676 }
2677
2678 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002679 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002680 /* UNLOCK */
2681 nc_server_ch_client_unlock(client);
2682 return -1;
2683 }
2684
2685 client->conn.persist.ka_max_wait = max_wait;
2686
2687 /* UNLOCK */
2688 nc_server_ch_client_unlock(client);
2689
2690 return 0;
2691}
2692
2693API int
2694nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2695{
2696 struct nc_ch_client *client;
2697
2698 if (!client_name) {
2699 ERRARG("client_name");
2700 return -1;
2701 }
2702
2703 /* LOCK */
2704 client = nc_server_ch_client_lock(client_name, 0, NULL);
2705 if (!client) {
2706 return -1;
2707 }
2708
2709 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002710 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002711 /* UNLOCK */
2712 nc_server_ch_client_unlock(client);
2713 return -1;
2714 }
2715
2716 client->conn.persist.ka_max_attempts = max_attempts;
2717
2718 /* UNLOCK */
2719 nc_server_ch_client_unlock(client);
2720
2721 return 0;
2722}
2723
2724API int
2725nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2726{
2727 struct nc_ch_client *client;
2728
2729 if (!client_name) {
2730 ERRARG("client_name");
2731 return -1;
2732 }
2733
2734 /* LOCK */
2735 client = nc_server_ch_client_lock(client_name, 0, NULL);
2736 if (!client) {
2737 return -1;
2738 }
2739
2740 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002741 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002742 /* UNLOCK */
2743 nc_server_ch_client_unlock(client);
2744 return -1;
2745 }
2746
2747 client->conn.period.idle_timeout = idle_timeout;
2748
2749 /* UNLOCK */
2750 nc_server_ch_client_unlock(client);
2751
2752 return 0;
2753}
2754
2755API int
2756nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2757{
2758 struct nc_ch_client *client;
2759
2760 if (!client_name) {
2761 ERRARG("client_name");
2762 return -1;
2763 } else if (!reconnect_timeout) {
2764 ERRARG("reconnect_timeout");
2765 return -1;
2766 }
2767
2768 /* LOCK */
2769 client = nc_server_ch_client_lock(client_name, 0, NULL);
2770 if (!client) {
2771 return -1;
2772 }
2773
2774 if (client->conn_type != NC_CH_PERIOD) {
2775 ERR("Call Home client \"%s\" is not of periodic connection type.");
2776 /* UNLOCK */
2777 nc_server_ch_client_unlock(client);
2778 return -1;
2779 }
2780
2781 client->conn.period.reconnect_timeout = reconnect_timeout;
2782
2783 /* UNLOCK */
2784 nc_server_ch_client_unlock(client);
2785
2786 return 0;
2787}
2788
2789API int
2790nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2791{
2792 struct nc_ch_client *client;
2793
2794 if (!client_name) {
2795 ERRARG("client_name");
2796 return -1;
2797 }
2798
2799 /* LOCK */
2800 client = nc_server_ch_client_lock(client_name, 0, NULL);
2801 if (!client) {
2802 return -1;
2803 }
2804
2805 client->start_with = start_with;
2806
2807 /* UNLOCK */
2808 nc_server_ch_client_unlock(client);
2809
2810 return 0;
2811}
2812
2813API int
2814nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2815{
2816 struct nc_ch_client *client;
2817
2818 if (!client_name) {
2819 ERRARG("client_name");
2820 return -1;
2821 } else if (!max_attempts) {
2822 ERRARG("max_attempts");
2823 return -1;
2824 }
2825
2826 /* LOCK */
2827 client = nc_server_ch_client_lock(client_name, 0, NULL);
2828 if (!client) {
2829 return -1;
2830 }
2831
2832 client->max_attempts = max_attempts;
2833
2834 /* UNLOCK */
2835 nc_server_ch_client_unlock(client);
2836
2837 return 0;
2838}
2839
2840/* client lock is expected to be held */
2841static NC_MSG_TYPE
2842nc_connect_ch_client_endpt(struct nc_ch_client *client, struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01002843{
Michal Vasko71090fc2016-05-24 16:37:28 +02002844 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002845 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002846 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01002847 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01002848
Michal Vasko66032bc2019-01-22 15:03:12 +01002849 sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002850 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002851 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002852 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00002853 /* no need to store the socket as pending any longer */
2854 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01002855
Michal Vasko131120a2018-05-29 15:44:02 +02002856 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002857 if (!(*session)) {
2858 ERRMEM;
2859 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01002860 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002861 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002862 }
2863 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01002864 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002865 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko66032bc2019-01-22 15:03:12 +01002866 (*session)->host = lydict_insert_zc(server_opts.ctx, ip_host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002867 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002868
Michal Vaskob05053d2016-01-22 16:12:06 +01002869 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002870#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002871 if (client->ti == NC_TI_LIBSSH) {
2872 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002873 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002874 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002875
Michal Vasko71090fc2016-05-24 16:37:28 +02002876 if (ret < 0) {
2877 msgtype = NC_MSG_ERROR;
2878 goto fail;
2879 } else if (!ret) {
2880 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002881 goto fail;
2882 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002883 } else
2884#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002885#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002886 if (client->ti == NC_TI_OPENSSL) {
2887 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002888 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002889 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002890
Michal Vasko71090fc2016-05-24 16:37:28 +02002891 if (ret < 0) {
2892 msgtype = NC_MSG_ERROR;
2893 goto fail;
2894 } else if (!ret) {
2895 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002896 goto fail;
2897 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002898 } else
2899#endif
2900 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002901 ERRINT;
2902 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002903 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002904 goto fail;
2905 }
2906
2907 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01002908 (*session)->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01002909
2910 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002911 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002912 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002913 goto fail;
2914 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002915
2916 nc_gettimespec_mono(&ts_cur);
2917 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2918 nc_gettimespec_real(&ts_cur);
2919 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01002920 (*session)->status = NC_STATUS_RUNNING;
2921
Michal Vasko71090fc2016-05-24 16:37:28 +02002922 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002923
2924fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002925 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002926 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002927 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002928}
2929
Michal Vasko2e6defd2016-10-07 15:48:15 +02002930struct nc_ch_client_thread_arg {
2931 char *client_name;
2932 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2933};
2934
2935static struct nc_ch_client *
2936nc_server_ch_client_with_endpt_lock(const char *name)
2937{
2938 struct nc_ch_client *client;
2939
2940 while (1) {
2941 /* LOCK */
2942 client = nc_server_ch_client_lock(name, 0, NULL);
2943 if (!client) {
2944 return NULL;
2945 }
2946 if (client->ch_endpt_count) {
2947 return client;
2948 }
2949 /* no endpoints defined yet */
2950
2951 /* UNLOCK */
2952 nc_server_ch_client_unlock(client);
2953
2954 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2955 }
2956
2957 return NULL;
2958}
2959
2960static int
2961nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2962{
Michal Vasko3f05a092018-03-13 10:39:49 +01002963 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002964 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002965 struct timespec ts;
2966 struct nc_ch_client *client;
2967
2968 /* session created, initialize condition */
Michal Vasko27377422018-03-15 08:59:35 +01002969 session->opts.server.ch_lock = calloc(1, sizeof *session->opts.server.ch_lock);
2970 session->opts.server.ch_cond = calloc(1, sizeof *session->opts.server.ch_cond);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002971 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2972 ERRMEM;
2973 nc_session_free(session, NULL);
2974 return -1;
2975 }
2976 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2977 pthread_cond_init(session->opts.server.ch_cond, NULL);
2978
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002979 session->flags |= NC_SESSION_CALLHOME;
2980
Michal Vasko2e6defd2016-10-07 15:48:15 +02002981 /* CH LOCK */
2982 pthread_mutex_lock(session->opts.server.ch_lock);
2983
2984 /* give the session to the user */
2985 data->session_clb(data->client_name, session);
2986
2987 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02002988 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01002989 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002990
Michal Vasko3f05a092018-03-13 10:39:49 +01002991 r = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2992 if (!r) {
2993 /* we were woken up, something probably happened */
2994 if (session->status != NC_STATUS_RUNNING) {
2995 break;
2996 }
2997 } else if (r != ETIMEDOUT) {
2998 ERR("Pthread condition timedwait failed (%s).", strerror(r));
2999 ret = -1;
3000 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003001 }
3002
Michal Vasko2e6defd2016-10-07 15:48:15 +02003003 /* check whether the client was not removed */
3004 /* LOCK */
3005 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
3006 if (!client) {
3007 /* client was removed, finish thread */
3008 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
3009 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003010 ret = 1;
3011 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003012 }
3013
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003014 if (client->conn_type == NC_CH_PERSIST) {
3015 /* TODO keep-alives */
3016 idle_timeout = client->conn.persist.idle_timeout;
3017 } else {
3018 idle_timeout = client->conn.period.idle_timeout;
3019 }
3020
Michal Vasko9fb42272017-10-05 13:50:05 +02003021 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003022 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 +02003023 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
3024 session->status = NC_STATUS_INVALID;
3025 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3026 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003027
3028 /* UNLOCK */
3029 nc_server_ch_client_unlock(client);
3030
3031 } while (session->status == NC_STATUS_RUNNING);
3032
Michal Vasko27377422018-03-15 08:59:35 +01003033 /* CH UNLOCK */
3034 pthread_mutex_unlock(session->opts.server.ch_lock);
3035
Michal Vasko3f05a092018-03-13 10:39:49 +01003036 if (session->status == NC_STATUS_CLOSING) {
3037 /* signal to nc_session_free() that we registered session being freed, otherwise it matters not */
3038 session->flags &= ~NC_SESSION_CALLHOME;
3039 }
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003040
Michal Vasko3f05a092018-03-13 10:39:49 +01003041 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003042}
3043
3044static void *
3045nc_ch_client_thread(void *arg)
3046{
3047 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3048 NC_MSG_TYPE msgtype;
3049 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003050 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003051 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003052 struct nc_ch_endpt *cur_endpt;
3053 struct nc_session *session;
3054 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003055 uint32_t client_id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003056
3057 /* LOCK */
3058 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3059 if (!client) {
3060 goto cleanup;
3061 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003062 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003063
3064 cur_endpt = &client->ch_endpts[0];
3065 cur_endpt_name = strdup(cur_endpt->name);
3066
Michal Vasko29af44b2016-10-13 10:59:55 +02003067 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003068 while (1) {
3069 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
3070
3071 if (msgtype == NC_MSG_HELLO) {
3072 /* UNLOCK */
3073 nc_server_ch_client_unlock(client);
3074
Michal Vasko29af44b2016-10-13 10:59:55 +02003075 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003076 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3077 goto cleanup;
3078 }
Michal Vasko29af44b2016-10-13 10:59:55 +02003079 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003080
3081 /* LOCK */
3082 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3083 if (!client) {
3084 goto cleanup;
3085 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003086 if (client->id != client_id) {
3087 nc_server_ch_client_unlock(client);
3088 goto cleanup;
3089 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003090
3091 /* session changed status -> it was disconnected for whatever reason,
3092 * persistent connection immediately tries to reconnect, periodic waits some first */
3093 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003094 /* UNLOCK */
3095 nc_server_ch_client_unlock(client);
3096
3097 /* TODO wake up sometimes to check for new notifications */
3098 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
3099
3100 /* LOCK */
3101 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3102 if (!client) {
3103 goto cleanup;
3104 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003105 if (client->id != client_id) {
3106 nc_server_ch_client_unlock(client);
3107 goto cleanup;
3108 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003109 }
3110
3111 /* set next endpoint to try */
3112 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003113 next_endpt_index = 0;
Michal Vasko2a225342018-09-05 08:38:34 +02003114 } else {
Peter Feiged05f2252018-09-03 08:09:47 +00003115 /* we keep the current one but due to unlock/lock we have to find it again */
3116 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3117 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3118 break;
3119 }
3120 }
3121 if (next_endpt_index >= client->ch_endpt_count) {
3122 /* endpoint was removed, start with the first one */
3123 next_endpt_index = 0;
3124 }
3125 }
3126
Michal Vasko2e6defd2016-10-07 15:48:15 +02003127 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003128 /* UNLOCK */
3129 nc_server_ch_client_unlock(client);
3130
Michal Vasko2e6defd2016-10-07 15:48:15 +02003131 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003132 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3133
Michal Vasko6bb116b2016-10-26 13:53:46 +02003134 /* LOCK */
3135 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3136 if (!client) {
3137 goto cleanup;
3138 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003139 if (client->id != client_id) {
3140 nc_server_ch_client_unlock(client);
3141 goto cleanup;
3142 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003143
Michal Vasko2e6defd2016-10-07 15:48:15 +02003144 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003145
3146 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003147 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3148 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003149 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003150 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003151 }
3152
Peter Feiged05f2252018-09-03 08:09:47 +00003153 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003154 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003155 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003156 cur_attempts = 0;
3157 } else if (cur_attempts == client->max_attempts) {
3158 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003159 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003160 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003161 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003162 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003163 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003164 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003165 }
3166
3167 cur_attempts = 0;
3168 } /* else we keep the current one */
3169 }
Peter Feiged05f2252018-09-03 08:09:47 +00003170
3171 cur_endpt = &client->ch_endpts[next_endpt_index];
3172 free(cur_endpt_name);
3173 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003174 }
3175
3176cleanup:
3177 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003178 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003179 free(data->client_name);
3180 free(data);
3181 return NULL;
3182}
3183
3184API int
3185nc_connect_ch_client_dispatch(const char *client_name,
Michal Vasko3f05a092018-03-13 10:39:49 +01003186 void (*session_clb)(const char *client_name, struct nc_session *new_session))
3187{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003188 int ret;
3189 pthread_t tid;
3190 struct nc_ch_client_thread_arg *arg;
3191
3192 if (!client_name) {
3193 ERRARG("client_name");
3194 return -1;
3195 } else if (!session_clb) {
3196 ERRARG("session_clb");
3197 return -1;
3198 }
3199
3200 arg = malloc(sizeof *arg);
3201 if (!arg) {
3202 ERRMEM;
3203 return -1;
3204 }
3205 arg->client_name = strdup(client_name);
3206 if (!arg->client_name) {
3207 ERRMEM;
3208 free(arg);
3209 return -1;
3210 }
3211 arg->session_clb = session_clb;
3212
3213 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3214 if (ret) {
3215 ERR("Creating a new thread failed (%s).", strerror(ret));
3216 free(arg->client_name);
3217 free(arg);
3218 return -1;
3219 }
3220 /* the thread now manages arg */
3221
3222 pthread_detach(tid);
3223
3224 return 0;
3225}
3226
Radek Krejci53691be2016-02-22 13:58:37 +01003227#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003228
Michal Vaskoe8e07702017-03-15 10:19:30 +01003229API int
3230nc_server_endpt_count(void)
3231{
3232 return server_opts.endpt_count;
3233}
3234
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003235API time_t
3236nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003237{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003238 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003239 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003240 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003241 }
3242
Michal Vasko2e6defd2016-10-07 15:48:15 +02003243 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003244}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003245
3246API void
3247nc_session_set_notif_status(struct nc_session *session, int notif_status)
3248{
3249 if (!session || (session->side != NC_SERVER)) {
3250 ERRARG("session");
3251 return;
3252 }
3253
3254 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3255}
3256
3257API int
3258nc_session_get_notif_status(const struct nc_session *session)
3259{
3260 if (!session || (session->side != NC_SERVER)) {
3261 ERRARG("session");
3262 return 0;
3263 }
3264
3265 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003266}