blob: 3557f0c710aa8fdc5c38577f815ded1d20cd3cf3 [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 */
Miroslav Mareš9563b812017-08-19 17:45:36 +020014#define _GNU_SOURCE /* signals, threads */
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>
23#include <netinet/in.h>
24#include <arpa/inet.h>
25#include <unistd.h>
Michal Vasko0190bc32016-03-02 15:47:49 +010026#include <fcntl.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010027#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010028#include <time.h>
Michal Vaskoade892d2017-02-22 13:40:35 +010029#include <signal.h>
Michal Vasko086311b2016-01-08 09:53:11 +010030
Michal Vasko1a38c862016-01-15 15:50:07 +010031#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010032#include "session_server.h"
33
Michal Vaskob48aa812016-01-18 14:13:09 +010034struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010035#ifdef NC_ENABLED_SSH
36 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
37#endif
38 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020039 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
40 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010041};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010042
fanchanghu966f2de2016-07-21 02:28:57 -040043static nc_rpc_clb global_rpc_clb = NULL;
44
Michal Vasko3031aae2016-01-27 16:07:18 +010045struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010046nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010047{
48 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010049 struct nc_endpt *endpt = NULL;
50
Michal Vaskoade892d2017-02-22 13:40:35 +010051 /* WRITE LOCK */
52 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010053
54 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020055 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010056 endpt = &server_opts.endpts[i];
57 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010058 }
59 }
60
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010061 if (!endpt) {
62 ERR("Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010063 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020064 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010065 return NULL;
66 }
67
Michal Vaskoe2713da2016-08-22 16:06:40 +020068 if (idx) {
69 *idx = i;
70 }
71
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010072 return endpt;
73}
74
Michal Vasko2e6defd2016-10-07 15:48:15 +020075struct nc_ch_client *
76nc_server_ch_client_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010077{
Michal Vasko2e6defd2016-10-07 15:48:15 +020078 uint16_t i;
79 struct nc_ch_client *client = NULL;
80
81 /* READ LOCK */
82 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
83
84 for (i = 0; i < server_opts.ch_client_count; ++i) {
85 if (!strcmp(server_opts.ch_clients[i].name, name) && (!ti || (server_opts.ch_clients[i].ti == ti))) {
86 client = &server_opts.ch_clients[i];
87 break;
88 }
89 }
90
91 if (!client) {
92 ERR("Call Home client \"%s\" was not found.", name);
93 /* READ UNLOCK */
94 pthread_rwlock_unlock(&server_opts.ch_client_lock);
95 return NULL;
96 }
97
98 /* CH CLIENT LOCK */
99 pthread_mutex_lock(&client->lock);
100
101 if (idx) {
102 *idx = i;
103 }
104
105 return client;
106}
107
108void
109nc_server_ch_client_unlock(struct nc_ch_client *client)
110{
111 /* CH CLIENT UNLOCK */
112 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100113
114 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200115 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100116}
Michal Vasko086311b2016-01-08 09:53:11 +0100117
Michal Vasko1a38c862016-01-15 15:50:07 +0100118API void
119nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
120{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200121 if (!session) {
122 ERRARG("session");
123 return;
124 } else if (!reason) {
125 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100126 return;
127 }
128
Michal Vasko142cfea2017-08-07 10:12:11 +0200129 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
130 session->killed_by = 0;
131 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100132 session->term_reason = reason;
133}
134
Michal Vasko142cfea2017-08-07 10:12:11 +0200135API void
136nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
137{
138 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
139 ERRARG("session");
140 return;
141 } else if (!sid) {
142 ERRARG("sid");
143 return;
144 }
145
146 session->killed_by = sid;
147}
148
149API void
150nc_session_set_status(struct nc_session *session, NC_STATUS status)
151{
152 if (!session) {
153 ERRARG("session");
154 return;
155 } else if (!status) {
156 ERRARG("status");
157 return;
158 }
159
160 session->status = status;
161}
162
Michal Vasko086311b2016-01-08 09:53:11 +0100163int
Michal Vaskof05562c2016-01-20 12:06:43 +0100164nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100165{
166 const int optVal = 1;
167 const socklen_t optLen = sizeof(optVal);
168 int is_ipv4, sock;
169 struct sockaddr_storage saddr;
170
171 struct sockaddr_in *saddr4;
172 struct sockaddr_in6 *saddr6;
173
174
175 if (!strchr(address, ':')) {
176 is_ipv4 = 1;
177 } else {
178 is_ipv4 = 0;
179 }
180
181 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
182 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100183 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100184 goto fail;
185 }
186
187 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100188 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100189 goto fail;
190 }
191
192 bzero(&saddr, sizeof(struct sockaddr_storage));
193 if (is_ipv4) {
194 saddr4 = (struct sockaddr_in *)&saddr;
195
196 saddr4->sin_family = AF_INET;
197 saddr4->sin_port = htons(port);
198
199 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100200 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100201 goto fail;
202 }
203
204 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100205 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100206 goto fail;
207 }
208
209 } else {
210 saddr6 = (struct sockaddr_in6 *)&saddr;
211
212 saddr6->sin6_family = AF_INET6;
213 saddr6->sin6_port = htons(port);
214
215 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100216 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100217 goto fail;
218 }
219
220 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100221 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100222 goto fail;
223 }
224 }
225
Michal Vaskofb89d772016-01-08 12:25:35 +0100226 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100227 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100228 goto fail;
229 }
230
231 return sock;
232
233fail:
234 if (sock > -1) {
235 close(sock);
236 }
237
238 return -1;
239}
240
241int
Michal Vasko3031aae2016-01-27 16:07:18 +0100242nc_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 +0100243{
Michal Vaskof54cd352017-02-22 13:42:02 +0100244 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100245 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100246 struct pollfd *pfd;
247 struct sockaddr_storage saddr;
248 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100249 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100250
251 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100252 if (!pfd) {
253 ERRMEM;
254 return -1;
255 }
256
Michal Vaskoac2f6182017-01-30 14:32:03 +0100257 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200258 if (binds[i].sock < 0) {
259 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200260 continue;
261 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200262 if (binds[i].pollin) {
263 binds[i].pollin = 0;
264 /* leftover pollin */
265 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100266 break;
267 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100268 pfd[pfd_count].fd = binds[i].sock;
269 pfd[pfd_count].events = POLLIN;
270 pfd[pfd_count].revents = 0;
271
272 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100273 }
274
Michal Vasko0a3f3752016-10-13 14:58:38 +0200275 if (sock == -1) {
276 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100277 sigfillset(&sigmask);
278 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100279 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100280 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
281
Michal Vasko0a3f3752016-10-13 14:58:38 +0200282 if (!ret) {
283 /* we timeouted */
284 free(pfd);
285 return 0;
286 } else if (ret == -1) {
287 ERR("Poll failed (%s).", strerror(errno));
288 free(pfd);
289 return -1;
290 }
Michal Vasko086311b2016-01-08 09:53:11 +0100291
Michal Vaskoac2f6182017-01-30 14:32:03 +0100292 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
293 /* adjust i so that indices in binds and pfd always match */
294 while (binds[i].sock != pfd[j].fd) {
295 ++i;
296 }
297
298 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200299 --ret;
300
301 if (!ret) {
302 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100303 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200304 break;
305 } else {
306 /* just remember the event for next time */
307 binds[i].pollin = 1;
308 }
309 }
Michal Vasko086311b2016-01-08 09:53:11 +0100310 }
311 }
312 free(pfd);
313
314 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100315 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100316 return -1;
317 }
318
319 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100320 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100321 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100322 return -1;
323 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200324 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100325
Michal Vasko0190bc32016-03-02 15:47:49 +0100326 /* make the socket non-blocking */
327 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
328 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100329 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100330 return -1;
331 }
332
Michal Vasko3031aae2016-01-27 16:07:18 +0100333 if (idx) {
334 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100335 }
336
Michal Vasko086311b2016-01-08 09:53:11 +0100337 /* host was requested */
338 if (host) {
339 if (saddr.ss_family == AF_INET) {
340 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100341 if (*host) {
342 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
343 ERR("inet_ntop failed (%s).", strerror(errno));
344 free(*host);
345 *host = NULL;
346 }
Michal Vasko086311b2016-01-08 09:53:11 +0100347
Michal Vasko4eb3c312016-03-01 14:09:37 +0100348 if (port) {
349 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
350 }
351 } else {
352 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100353 }
354 } else if (saddr.ss_family == AF_INET6) {
355 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100356 if (*host) {
357 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
358 ERR("inet_ntop failed (%s).", strerror(errno));
359 free(*host);
360 *host = NULL;
361 }
Michal Vasko086311b2016-01-08 09:53:11 +0100362
Michal Vasko4eb3c312016-03-01 14:09:37 +0100363 if (port) {
364 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
365 }
366 } else {
367 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100368 }
369 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100370 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100371 }
372 }
373
374 return ret;
375}
376
Michal Vasko05ba9df2016-01-13 14:40:27 +0100377static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100378nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100379{
380 const char *identifier = NULL, *version = NULL, *format = NULL;
381 char *model_data = NULL;
382 const struct lys_module *module;
383 struct nc_server_error *err;
384 struct lyd_node *child, *data = NULL;
Michal Vasko88639e92017-08-03 14:38:10 +0200385 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100386
387 LY_TREE_FOR(rpc->child, child) {
388 if (!strcmp(child->schema->name, "identifier")) {
389 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
390 } else if (!strcmp(child->schema->name, "version")) {
391 version = ((struct lyd_node_leaf_list *)child)->value_str;
Radek Krejci1afa7792017-03-26 11:24:16 -0500392 if (version && version[0] == '\0') {
393 version = NULL;
394 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100395 } else if (!strcmp(child->schema->name, "format")) {
396 format = ((struct lyd_node_leaf_list *)child)->value_str;
397 }
398 }
399
400 /* check version */
401 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100402 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
403 nc_err_set_msg(err, "The requested version is not supported.", "en");
404 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100405 }
406
407 /* check and get module with the name identifier */
Radek Krejci3222b7d2017-09-21 16:04:30 +0200408 module = ly_ctx_get_module(server_opts.ctx, identifier, version, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100409 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200410 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
411 }
412 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100413 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
414 nc_err_set_msg(err, "The requested schema was not found.", "en");
415 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100416 }
417
418 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100419 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100420 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL, 0, 0);
Radek Krejci90fba642016-12-07 15:59:45 +0100421 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100422 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100423 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100424 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
425 nc_err_set_msg(err, "The requested format is not supported.", "en");
426 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100427 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200428 if (!model_data) {
429 ERRINT;
430 return NULL;
431 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100432
Michal Vasko88639e92017-08-03 14:38:10 +0200433 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/data", 1);
434 if (!sdata) {
435 ERRINT;
436 free(model_data);
437 return NULL;
438 }
439
Radek Krejci539efb62016-08-24 15:05:16 +0200440 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
441 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100442 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100443 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200444 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100445 return NULL;
446 }
447
Radek Krejci36dfdb32016-09-01 16:56:35 +0200448 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100449}
450
451static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100452nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100453{
Michal Vasko428087d2016-01-14 16:04:28 +0100454 session->term_reason = NC_SESSION_TERM_CLOSED;
455 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100456}
457
Michal Vasko086311b2016-01-08 09:53:11 +0100458API int
459nc_server_init(struct ly_ctx *ctx)
460{
Michal Vasko88639e92017-08-03 14:38:10 +0200461 const struct lys_node *rpc;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100462
Michal Vasko086311b2016-01-08 09:53:11 +0100463 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200464 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100465 return -1;
466 }
467
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100468 nc_init();
469
Michal Vasko05ba9df2016-01-13 14:40:27 +0100470 /* set default <get-schema> callback if not specified */
Michal Vasko88639e92017-08-03 14:38:10 +0200471 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
472 if (rpc && !rpc->priv) {
473 lys_set_private(rpc, nc_clb_default_get_schema);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100474 }
475
476 /* set default <close-session> callback if not specififed */
Michal Vasko88639e92017-08-03 14:38:10 +0200477 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session", 0);
478 if (rpc && !rpc->priv) {
479 lys_set_private(rpc, nc_clb_default_close_session);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100480 }
481
Michal Vasko086311b2016-01-08 09:53:11 +0100482 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100483
484 server_opts.new_session_id = 1;
485 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
486
Michal Vasko086311b2016-01-08 09:53:11 +0100487 return 0;
488}
489
Michal Vaskob48aa812016-01-18 14:13:09 +0100490API void
491nc_server_destroy(void)
492{
Radek Krejci658782b2016-12-04 22:04:55 +0100493 unsigned int i;
494
495 for (i = 0; i < server_opts.capabilities_count; i++) {
496 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
497 }
498 free(server_opts.capabilities);
Michal Vaskob48aa812016-01-18 14:13:09 +0100499 pthread_spin_destroy(&server_opts.sid_lock);
500
Radek Krejci53691be2016-02-22 13:58:37 +0100501#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100502 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100503#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100504#ifdef NC_ENABLED_SSH
505 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100506
507 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
508 server_opts.hostkey_data_free(server_opts.hostkey_data);
509 }
510#endif
511#ifdef NC_ENABLED_TLS
512 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
513 server_opts.server_cert_data_free(server_opts.server_cert_data);
514 }
515 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
516 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
517 }
Michal Vaskob48aa812016-01-18 14:13:09 +0100518#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100519 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100520}
521
Michal Vasko086311b2016-01-08 09:53:11 +0100522API int
523nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
524{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200525 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
526 ERRARG("basic_mode");
527 return -1;
528 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
529 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100530 return -1;
531 }
532
533 server_opts.wd_basic_mode = basic_mode;
534 server_opts.wd_also_supported = also_supported;
535 return 0;
536}
537
Michal Vasko1a38c862016-01-15 15:50:07 +0100538API void
Michal Vasko55f03972016-04-13 08:56:01 +0200539nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
540{
541 if (!basic_mode && !also_supported) {
542 ERRARG("basic_mode and also_supported");
543 return;
544 }
545
546 if (basic_mode) {
547 *basic_mode = server_opts.wd_basic_mode;
548 }
549 if (also_supported) {
550 *also_supported = server_opts.wd_also_supported;
551 }
552}
553
Michal Vasko55f03972016-04-13 08:56:01 +0200554API int
Radek Krejci658782b2016-12-04 22:04:55 +0100555nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200556{
Radek Krejci658782b2016-12-04 22:04:55 +0100557 const char **new;
558
559 if (!value || !value[0]) {
560 ERRARG("value must not be empty");
561 return EXIT_FAILURE;
562 }
563
564 server_opts.capabilities_count++;
565 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
566 if (!new) {
567 ERRMEM;
568 return EXIT_FAILURE;
569 }
570 server_opts.capabilities = new;
571 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
572
573 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200574}
575
Michal Vasko1a38c862016-01-15 15:50:07 +0100576API void
Michal Vasko086311b2016-01-08 09:53:11 +0100577nc_server_set_hello_timeout(uint16_t hello_timeout)
578{
Michal Vasko086311b2016-01-08 09:53:11 +0100579 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100580}
581
Michal Vasko55f03972016-04-13 08:56:01 +0200582API uint16_t
583nc_server_get_hello_timeout(void)
584{
585 return server_opts.hello_timeout;
586}
587
Michal Vasko1a38c862016-01-15 15:50:07 +0100588API void
Michal Vasko086311b2016-01-08 09:53:11 +0100589nc_server_set_idle_timeout(uint16_t idle_timeout)
590{
Michal Vasko086311b2016-01-08 09:53:11 +0100591 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100592}
593
Michal Vasko55f03972016-04-13 08:56:01 +0200594API uint16_t
595nc_server_get_idle_timeout(void)
596{
597 return server_opts.idle_timeout;
598}
599
Michal Vasko71090fc2016-05-24 16:37:28 +0200600API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100601nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100602{
Michal Vasko71090fc2016-05-24 16:37:28 +0200603 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200604 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200605
Michal Vasko45e53ae2016-04-07 11:46:03 +0200606 if (!server_opts.ctx) {
607 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200608 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200609 } else if (fdin < 0) {
610 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200611 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200612 } else if (fdout < 0) {
613 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200614 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200615 } else if (!username) {
616 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200617 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200618 } else if (!session) {
619 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200620 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100621 }
622
623 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +0100624 *session = nc_new_session(0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100625 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100626 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200627 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100628 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100629 (*session)->status = NC_STATUS_STARTING;
630 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100631
Michal Vaskoade892d2017-02-22 13:40:35 +0100632 /* transport lock */
633 pthread_mutex_init((*session)->ti_lock, NULL);
634 pthread_cond_init((*session)->ti_cond, NULL);
635 *(*session)->ti_inuse = 0;
636
Michal Vasko086311b2016-01-08 09:53:11 +0100637 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100638 (*session)->ti_type = NC_TI_FD;
639 (*session)->ti.fd.in = fdin;
640 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100641
642 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100643 (*session)->flags = NC_SESSION_SHAREDCTX;
644 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100645
Michal Vaskob48aa812016-01-18 14:13:09 +0100646 /* assign new SID atomically */
647 pthread_spin_lock(&server_opts.sid_lock);
648 (*session)->id = server_opts.new_session_id++;
649 pthread_spin_unlock(&server_opts.sid_lock);
650
Michal Vasko086311b2016-01-08 09:53:11 +0100651 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200652 msgtype = nc_handshake(*session);
653 if (msgtype != NC_MSG_HELLO) {
654 nc_session_free(*session, NULL);
655 *session = NULL;
656 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100657 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200658
659 nc_gettimespec_mono(&ts_cur);
660 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
661 nc_gettimespec_real(&ts_cur);
662 (*session)->opts.server.session_start = ts_cur.tv_sec;
663
Michal Vasko1a38c862016-01-15 15:50:07 +0100664 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100665
Michal Vasko71090fc2016-05-24 16:37:28 +0200666 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100667}
Michal Vasko9e036d52016-01-08 10:49:26 +0100668
Michal Vaskob30b99c2016-07-26 11:35:43 +0200669static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100670nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
671{
672 uint8_t q_last;
673
674 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
675 ERRINT;
676 return;
677 }
678
679 /* get a unique queue value (by adding 1 to the last added value, if any) */
680 if (ps->queue_len) {
681 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
682 *id = ps->queue[q_last] + 1;
683 } else {
684 *id = 0;
685 }
686
687 /* add the id into the queue */
688 ++ps->queue_len;
689 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
690 ps->queue[q_last] = *id;
691}
692
693static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200694nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
695{
Michal Vasko74c345f2018-02-07 10:37:11 +0100696 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200697
698 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100699 /* get the actual queue idx */
700 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200701
702 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100703 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200704 /* another equal value, simply cannot be */
705 ERRINT;
706 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100707 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200708 /* found our id, there can be no more equal valid values */
709 found = 1;
710 }
711 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200712 if (!found) {
713 ERRINT;
714 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100715
716 /* remove the id by moving the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200717 --ps->queue_len;
Michal Vasko74c345f2018-02-07 10:37:11 +0100718 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200719}
720
Michal Vaskof04a52a2016-04-07 10:52:10 +0200721int
Michal Vasko26043172016-07-26 14:08:59 +0200722nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200723{
724 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200725 struct timespec ts;
726
Michal Vasko77a6abe2017-10-05 10:02:20 +0200727 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100728 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200729
730 /* LOCK */
731 ret = pthread_mutex_timedlock(&ps->lock, &ts);
732 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200733 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200734 return -1;
735 }
736
Michal Vasko74c345f2018-02-07 10:37:11 +0100737 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +0100738 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100739 ERR("%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +0100740 pthread_mutex_unlock(&ps->lock);
741 return -1;
742 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100743
744 /* add ourselves into the queue */
745 nc_ps_queue_add_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200746
747 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200748 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200749 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100750 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200751
752 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
753 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200754 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200755 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200756 nc_ps_queue_remove_id(ps, *id);
757 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200758 return -1;
759 }
760 }
761
Michal Vaskobe86fe32016-04-07 10:43:03 +0200762 /* UNLOCK */
763 pthread_mutex_unlock(&ps->lock);
764
765 return 0;
766}
767
Michal Vaskof04a52a2016-04-07 10:52:10 +0200768int
Michal Vasko26043172016-07-26 14:08:59 +0200769nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200770{
771 int ret;
772 struct timespec ts;
773
Michal Vasko77a6abe2017-10-05 10:02:20 +0200774 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100775 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200776
777 /* LOCK */
778 ret = pthread_mutex_timedlock(&ps->lock, &ts);
779 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200780 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200781 ret = -1;
782 }
783
Michal Vaskob30b99c2016-07-26 11:35:43 +0200784 /* we must be the first, it was our turn after all, right? */
785 if (ps->queue[ps->queue_begin] != id) {
786 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200787 /* UNLOCK */
788 if (!ret) {
789 pthread_mutex_unlock(&ps->lock);
790 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200791 return -1;
792 }
793
Michal Vaskobe86fe32016-04-07 10:43:03 +0200794 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200795 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200796
797 /* broadcast to all other threads that the queue moved */
798 pthread_cond_broadcast(&ps->cond);
799
Michal Vaskobe86fe32016-04-07 10:43:03 +0200800 /* UNLOCK */
801 if (!ret) {
802 pthread_mutex_unlock(&ps->lock);
803 }
804
805 return ret;
806}
807
Michal Vasko428087d2016-01-14 16:04:28 +0100808API struct nc_pollsession *
809nc_ps_new(void)
810{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100811 struct nc_pollsession *ps;
812
813 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100814 if (!ps) {
815 ERRMEM;
816 return NULL;
817 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200818 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100819 pthread_mutex_init(&ps->lock, NULL);
820
821 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100822}
823
824API void
825nc_ps_free(struct nc_pollsession *ps)
826{
fanchanghu3d4e7212017-08-09 09:42:30 +0800827 uint16_t i;
828
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100829 if (!ps) {
830 return;
831 }
832
Michal Vaskobe86fe32016-04-07 10:43:03 +0200833 if (ps->queue_len) {
834 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
835 }
836
fanchanghu3d4e7212017-08-09 09:42:30 +0800837 for (i = 0; i < ps->session_count; i++) {
838 free(ps->sessions[i]);
839 }
840
Michal Vasko428087d2016-01-14 16:04:28 +0100841 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100842 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200843 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100844
Michal Vasko428087d2016-01-14 16:04:28 +0100845 free(ps);
846}
847
848API int
849nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
850{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200851 uint8_t q_id;
852
Michal Vasko45e53ae2016-04-07 11:46:03 +0200853 if (!ps) {
854 ERRARG("ps");
855 return -1;
856 } else if (!session) {
857 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100858 return -1;
859 }
860
Michal Vasko48a63ed2016-03-01 09:48:21 +0100861 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200862 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200863 return -1;
864 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100865
Michal Vasko428087d2016-01-14 16:04:28 +0100866 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100867 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +0100868 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100869 ERRMEM;
870 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200871 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100872 return -1;
873 }
fanchanghu3d4e7212017-08-09 09:42:30 +0800874 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
875 if (!ps->sessions[ps->session_count - 1]) {
876 ERRMEM;
877 --ps->session_count;
878 /* UNLOCK */
879 nc_ps_unlock(ps, q_id, __func__);
880 return -1;
881 }
882 ps->sessions[ps->session_count - 1]->session = session;
883 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +0100884
Michal Vasko48a63ed2016-03-01 09:48:21 +0100885 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200886 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100887}
888
Michal Vasko48a63ed2016-03-01 09:48:21 +0100889static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100890_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100891{
892 uint16_t i;
893
Radek Krejcid5f978f2016-03-03 13:14:45 +0100894 if (index >= 0) {
895 i = (uint16_t)index;
896 goto remove;
897 }
Michal Vasko428087d2016-01-14 16:04:28 +0100898 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +0800899 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100900remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100901 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +0800902 if (i <= ps->session_count) {
903 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +0100904 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +0800905 }
906 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +0100907 free(ps->sessions);
908 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +0100909 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +0100910 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100911 return 0;
912 }
913 }
914
Michal Vaskof0537d82016-01-29 14:42:38 +0100915 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100916}
917
Michal Vasko48a63ed2016-03-01 09:48:21 +0100918API int
919nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
920{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200921 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200922 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100923
Michal Vasko45e53ae2016-04-07 11:46:03 +0200924 if (!ps) {
925 ERRARG("ps");
926 return -1;
927 } else if (!session) {
928 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100929 return -1;
930 }
931
932 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200933 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200934 return -1;
935 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100936
Radek Krejcid5f978f2016-03-03 13:14:45 +0100937 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100938
939 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200940 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100941
Michal Vaskobe86fe32016-04-07 10:43:03 +0200942 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100943}
944
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100945API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +0200946nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100947{
948 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100949 struct nc_session *ret = NULL;
950
951 if (!ps) {
952 ERRARG("ps");
953 return NULL;
954 }
955
956 /* LOCK */
957 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
958 return NULL;
959 }
960
Michal Vasko4871c9d2017-10-09 14:48:39 +0200961 if (idx < ps->session_count) {
962 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100963 }
964
965 /* UNLOCK */
966 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
967
968 return ret;
969}
970
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100971API uint16_t
972nc_ps_session_count(struct nc_pollsession *ps)
973{
974 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200975 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100976 return 0;
977 }
978
Michal Vaskof4462fd2017-02-15 14:29:05 +0100979 return ps->session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100980}
981
Michal Vasko71090fc2016-05-24 16:37:28 +0200982/* must be called holding the session lock!
983 * returns: NC_PSPOLL_ERROR,
984 * NC_PSPOLL_BAD_RPC,
985 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
986 * NC_PSPOLL_RPC
987 */
988static int
Radek Krejci93e80222016-10-03 13:34:25 +0200989nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100990{
991 struct lyxml_elem *xml = NULL;
992 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200993 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200994 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100995
Michal Vasko45e53ae2016-04-07 11:46:03 +0200996 if (!session) {
997 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200998 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200999 } else if (!rpc) {
1000 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001001 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001002 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001003 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001004 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001005 }
1006
1007 msgtype = nc_read_msg(session, &xml);
1008
1009 switch (msgtype) {
1010 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001011 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001012 if (!*rpc) {
1013 ERRMEM;
1014 goto error;
1015 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001016
Radek Krejcif93c7d42016-04-06 13:41:15 +02001017 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001018 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1019 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001020 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001021 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +02001022 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +02001023 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001024 nc_server_reply_free(reply);
1025 if (ret == -1) {
1026 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001027 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001028 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1029 } else {
1030 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001031 }
Michal Vasko428087d2016-01-14 16:04:28 +01001032 (*rpc)->root = xml;
1033 break;
1034 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001035 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001036 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001037 goto error;
1038 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001039 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001040 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001041 goto error;
1042 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001043 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001044 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001045 goto error;
1046 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001047 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +01001048 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
1049 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001050 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001051 break;
1052 }
1053
Michal Vasko71090fc2016-05-24 16:37:28 +02001054 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001055
1056error:
1057 /* cleanup */
1058 lyxml_free(server_opts.ctx, xml);
1059
Michal Vasko71090fc2016-05-24 16:37:28 +02001060 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001061}
1062
fanchanghu966f2de2016-07-21 02:28:57 -04001063API void
1064nc_set_global_rpc_clb(nc_rpc_clb clb)
1065{
1066 global_rpc_clb = clb;
1067}
1068
Radek Krejci93e80222016-10-03 13:34:25 +02001069API NC_MSG_TYPE
1070nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1071{
1072 NC_MSG_TYPE result = NC_MSG_NOTIF;
1073 int ret;
1074
1075 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001076 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001077 ERRARG("session");
1078 return NC_MSG_ERROR;
1079 } else if (!notif || !notif->tree || !notif->eventtime) {
1080 ERRARG("notif");
1081 return NC_MSG_ERROR;
1082 }
1083
1084 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vaskoade892d2017-02-22 13:40:35 +01001085 ret = nc_session_lock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001086 if (ret < 0) {
1087 return NC_MSG_ERROR;
1088 } else if (!ret) {
1089 return NC_MSG_WOULDBLOCK;
1090 }
1091
1092 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1093 if (ret == -1) {
1094 ERR("Session %u: failed to write notification.", session->id);
1095 result = NC_MSG_ERROR;
1096 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001097
1098 nc_session_unlock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001099
1100 return result;
1101}
1102
Michal Vasko71090fc2016-05-24 16:37:28 +02001103/* must be called holding the session lock!
1104 * returns: NC_PSPOLL_ERROR,
1105 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1106 * NC_PSPOLL_REPLY_ERROR,
1107 * 0
1108 */
1109static int
Radek Krejci93e80222016-10-03 13:34:25 +02001110nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001111{
1112 nc_rpc_clb clb;
1113 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001114 struct lys_node *rpc_act = NULL;
1115 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001116 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001117
Michal Vasko4a827e52016-03-03 10:59:00 +01001118 if (!rpc) {
1119 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001120 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001121 }
1122
Michal Vasko90e8e692016-07-13 12:27:57 +02001123 if (rpc->tree->schema->nodetype == LYS_RPC) {
1124 /* RPC */
1125 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001126 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001127 /* action */
1128 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1129 if (elem->schema->nodetype == LYS_ACTION) {
1130 rpc_act = elem->schema;
1131 break;
1132 }
1133 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001134 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001135 if (!rpc_act) {
1136 ERRINT;
1137 return NC_PSPOLL_ERROR;
1138 }
1139 }
1140
1141 if (!rpc_act->priv) {
1142 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001143 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001144 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001145 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001146 reply = clb(rpc->tree, session);
1147 }
1148
1149 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001150 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001151 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001152 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1153 if (reply->type == NC_RPL_ERROR) {
1154 ret |= NC_PSPOLL_REPLY_ERROR;
1155 }
1156 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001157
Michal Vasko71090fc2016-05-24 16:37:28 +02001158 if (r == -1) {
1159 ERR("Session %u: failed to write reply.", session->id);
1160 ret |= NC_PSPOLL_ERROR;
1161 }
Michal Vasko428087d2016-01-14 16:04:28 +01001162
1163 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1164 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1165 session->status = NC_STATUS_INVALID;
1166 }
1167
Michal Vasko71090fc2016-05-24 16:37:28 +02001168 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001169}
1170
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001171/* session must be running and session lock held!
1172 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1173 * NC_PSPOLL_ERROR, (msg filled)
1174 * NC_PSPOLL_TIMEOUT,
1175 * NC_PSPOLL_RPC (some application data available),
1176 * NC_PSPOLL_SSH_CHANNEL,
1177 * NC_PSPOLL_SSH_MSG
1178 */
1179static int
Michal Vasko9fb42272017-10-05 13:50:05 +02001180nc_ps_poll_session(struct nc_session *session, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001181{
Michal Vasko9a327362017-01-11 11:31:46 +01001182 struct pollfd pfd;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001183 int r, ret;
Michal Vasko9a327362017-01-11 11:31:46 +01001184#ifdef NC_ENABLED_SSH
1185 struct nc_session *new;
1186#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001187
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001188 /* check timeout first */
1189 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001190 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001191 sprintf(msg, "session idle timeout elapsed");
1192 session->status = NC_STATUS_INVALID;
1193 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1194 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1195 }
1196
1197 switch (session->ti_type) {
1198#ifdef NC_ENABLED_SSH
1199 case NC_TI_LIBSSH:
1200 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001201 if (r == SSH_EOF) {
1202 sprintf(msg, "SSH channel unexpected EOF");
1203 session->status = NC_STATUS_INVALID;
1204 session->term_reason = NC_SESSION_TERM_DROPPED;
1205 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1206 } else if (r == SSH_ERROR) {
1207 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001208 session->status = NC_STATUS_INVALID;
1209 session->term_reason = NC_SESSION_TERM_OTHER;
1210 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001211 } else if (!r) {
1212 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1213 /* new SSH message */
1214 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1215 if (session->ti.libssh.next) {
1216 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1217 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1218 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1219 /* new NETCONF SSH channel */
1220 ret = NC_PSPOLL_SSH_CHANNEL;
1221 break;
1222 }
1223 }
1224 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001225 break;
1226 }
1227 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001228
Michal Vasko8dcaa882017-10-19 14:28:42 +02001229 /* just some SSH message */
1230 ret = NC_PSPOLL_SSH_MSG;
1231 } else {
1232 ret = NC_PSPOLL_TIMEOUT;
1233 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001234 } else {
1235 /* we have some application data */
1236 ret = NC_PSPOLL_RPC;
1237 }
1238 break;
1239#endif
1240#ifdef NC_ENABLED_TLS
1241 case NC_TI_OPENSSL:
1242 r = SSL_pending(session->ti.tls);
1243 if (!r) {
1244 /* no data pending in the SSL buffer, poll fd */
1245 pfd.fd = SSL_get_rfd(session->ti.tls);
1246 if (pfd.fd < 0) {
1247 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1248 ret = NC_PSPOLL_ERROR;
1249 break;
1250 }
1251 pfd.events = POLLIN;
1252 pfd.revents = 0;
1253 r = poll(&pfd, 1, 0);
1254
1255 if ((r < 0) && (errno != EINTR)) {
1256 sprintf(msg, "poll failed (%s)", strerror(errno));
1257 session->status = NC_STATUS_INVALID;
1258 ret = NC_PSPOLL_ERROR;
1259 } else if (r > 0) {
1260 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1261 sprintf(msg, "communication socket unexpectedly closed");
1262 session->status = NC_STATUS_INVALID;
1263 session->term_reason = NC_SESSION_TERM_DROPPED;
1264 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1265 } else if (pfd.revents & POLLERR) {
1266 sprintf(msg, "communication socket error");
1267 session->status = NC_STATUS_INVALID;
1268 session->term_reason = NC_SESSION_TERM_OTHER;
1269 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1270 } else {
1271 ret = NC_PSPOLL_RPC;
1272 }
1273 } else {
1274 ret = NC_PSPOLL_TIMEOUT;
1275 }
1276 } else {
1277 ret = NC_PSPOLL_RPC;
1278 }
1279 break;
1280#endif
1281 case NC_TI_FD:
1282 pfd.fd = session->ti.fd.in;
1283 pfd.events = POLLIN;
1284 pfd.revents = 0;
1285 r = poll(&pfd, 1, 0);
1286
1287 if ((r < 0) && (errno != EINTR)) {
1288 sprintf(msg, "poll failed (%s)", strerror(errno));
1289 session->status = NC_STATUS_INVALID;
1290 ret = NC_PSPOLL_ERROR;
1291 } else if (r > 0) {
1292 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1293 sprintf(msg, "communication socket unexpectedly closed");
1294 session->status = NC_STATUS_INVALID;
1295 session->term_reason = NC_SESSION_TERM_DROPPED;
1296 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1297 } else if (pfd.revents & POLLERR) {
1298 sprintf(msg, "communication socket error");
1299 session->status = NC_STATUS_INVALID;
1300 session->term_reason = NC_SESSION_TERM_OTHER;
1301 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1302 } else {
1303 ret = NC_PSPOLL_RPC;
1304 }
1305 } else {
1306 ret = NC_PSPOLL_TIMEOUT;
1307 }
1308 break;
1309 case NC_TI_NONE:
1310 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1311 ret = NC_PSPOLL_ERROR;
1312 break;
1313 }
1314
1315 return ret;
1316}
1317
1318API int
1319nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1320{
1321 int ret, r;
1322 uint8_t q_id;
1323 uint16_t i, j;
1324 char msg[256];
1325 struct timespec ts_timeout, ts_cur;
1326 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001327 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001328 struct nc_server_rpc *rpc = NULL;
1329
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001330 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001331 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001332 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001333 }
1334
Michal Vaskoade892d2017-02-22 13:40:35 +01001335 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001336 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001337 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001338 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001339
Michal Vaskoade892d2017-02-22 13:40:35 +01001340 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001341 nc_ps_unlock(ps, q_id, __func__);
1342 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001343 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001344
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001345 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001346 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001347 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001348 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001349 nc_addtimespec(&ts_timeout, timeout);
1350 }
1351
1352 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001353 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001354 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001355 if (ps->last_event_session == ps->session_count - 1) {
1356 i = j = 0;
1357 } else {
1358 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001359 }
Michal Vasko9a327362017-01-11 11:31:46 +01001360 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001361 cur_ps_session = ps->sessions[i];
1362 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001363
Michal Vaskoade892d2017-02-22 13:40:35 +01001364 /* SESSION LOCK */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001365 r = nc_session_lock(cur_session, 0, __func__);
1366 if (r == -1) {
1367 ret = NC_PSPOLL_ERROR;
1368 } else if (r == 1) {
1369 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001370 switch (cur_ps_session->state) {
1371 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001372 if (cur_session->status == NC_STATUS_RUNNING) {
1373 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001374 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001375
1376 ret = nc_ps_poll_session(cur_session, ts_cur.tv_sec, msg);
1377 switch (ret) {
1378 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1379 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001380 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001381 break;
1382 case NC_PSPOLL_ERROR:
1383 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001384 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001385 break;
1386 case NC_PSPOLL_TIMEOUT:
1387#ifdef NC_ENABLED_SSH
1388 case NC_PSPOLL_SSH_CHANNEL:
1389 case NC_PSPOLL_SSH_MSG:
1390#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001391 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001392 break;
1393 case NC_PSPOLL_RPC:
1394 /* let's keep the state busy, we are not done with this session */
1395 break;
1396 }
1397 } else {
1398 /* session is not fine, let the caller know */
1399 ret = NC_PSPOLL_SESSION_TERM;
1400 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1401 ret |= NC_PSPOLL_SESSION_ERROR;
1402 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001403 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001404 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001405 break;
1406 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001407 /* it definitely should not be busy because we have the lock */
1408 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001409 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001410 break;
1411 case NC_PS_STATE_INVALID:
1412 /* we got it locked, but it will be freed, let it be */
1413 ret = NC_PSPOLL_TIMEOUT;
1414 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001415 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001416
1417 /* keep the session locked only in this one case */
1418 if (ret != NC_PSPOLL_RPC) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001419 /* SESSION UNLOCK */
1420 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001421 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001422 } else {
1423 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001424 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001425 }
Michal Vasko428087d2016-01-14 16:04:28 +01001426
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001427 /* something happened */
1428 if (ret != NC_PSPOLL_TIMEOUT) {
1429 break;
1430 }
1431
Michal Vasko9a327362017-01-11 11:31:46 +01001432 if (i == ps->session_count - 1) {
1433 i = 0;
1434 } else {
1435 ++i;
1436 }
1437 } while (i != j);
1438
Michal Vaskoade892d2017-02-22 13:40:35 +01001439 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001440 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001441 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001442 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001443 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001444
1445 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1446 /* final timeout */
1447 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001448 }
Michal Vasko428087d2016-01-14 16:04:28 +01001449 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001450 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001451
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001452 /* do we want to return the session? */
1453 switch (ret) {
1454 case NC_PSPOLL_RPC:
1455 case NC_PSPOLL_SESSION_TERM:
1456 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1457#ifdef NC_ENABLED_SSH
1458 case NC_PSPOLL_SSH_CHANNEL:
1459 case NC_PSPOLL_SSH_MSG:
1460#endif
1461 if (session) {
1462 *session = cur_session;
1463 }
1464 ps->last_event_session = i;
1465 break;
1466 default:
1467 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001468 }
Michal Vasko428087d2016-01-14 16:04:28 +01001469
Michal Vaskoade892d2017-02-22 13:40:35 +01001470 /* PS UNLOCK */
1471 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001472
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001473 /* we have some data available and the session is locked */
1474 if (ret == NC_PSPOLL_RPC) {
1475 ret = nc_server_recv_rpc(cur_session, &rpc);
1476 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1477 if (cur_session->status != NC_STATUS_RUNNING) {
1478 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001479 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001480 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001481 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001482 }
1483 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001484 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001485
1486 /* process RPC, not needed afterwards */
1487 ret |= nc_server_send_reply(cur_session, rpc);
1488 nc_server_rpc_free(rpc, server_opts.ctx);
1489
1490 if (cur_session->status != NC_STATUS_RUNNING) {
1491 ret |= NC_PSPOLL_SESSION_TERM;
1492 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1493 ret |= NC_PSPOLL_SESSION_ERROR;
1494 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001495 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001496 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001497 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001498 }
Michal Vasko428087d2016-01-14 16:04:28 +01001499 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001500
1501 /* SESSION UNLOCK */
1502 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001503 }
1504
Michal Vasko48a63ed2016-03-01 09:48:21 +01001505 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001506}
1507
Michal Vaskod09eae62016-02-01 10:32:52 +01001508API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001509nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001510{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001511 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001512 uint16_t i;
1513 struct nc_session *session;
1514
Michal Vasko9a25e932016-02-01 10:36:42 +01001515 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001516 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001517 return;
1518 }
1519
Michal Vasko48a63ed2016-03-01 09:48:21 +01001520 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001521 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001522 return;
1523 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001524
Michal Vasko48a63ed2016-03-01 09:48:21 +01001525 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001526 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001527 nc_session_free(ps->sessions[i]->session, data_free);
1528 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001529 }
1530 free(ps->sessions);
1531 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001532 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001533 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001534 } else {
1535 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001536 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1537 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001538 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001539 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001540 continue;
1541 }
1542
1543 ++i;
1544 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001545 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001546
1547 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001548 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001549}
1550
Radek Krejci53691be2016-02-22 13:58:37 +01001551#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001552
Michal Vaskoe2713da2016-08-22 16:06:40 +02001553API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001554nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001555{
Michal Vasko3031aae2016-01-27 16:07:18 +01001556 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001557 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001558
Michal Vasko45e53ae2016-04-07 11:46:03 +02001559 if (!name) {
1560 ERRARG("name");
1561 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001562 }
1563
Michal Vaskoade892d2017-02-22 13:40:35 +01001564 /* BIND LOCK */
1565 pthread_mutex_lock(&server_opts.bind_lock);
1566
1567 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001568 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001569
1570 /* check name uniqueness */
1571 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001572 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001573 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001574 ret = -1;
1575 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001576 }
1577 }
1578
Michal Vasko3031aae2016-01-27 16:07:18 +01001579 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001580 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001581 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001582 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001583 ret = -1;
1584 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001585 }
1586 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001587 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001588
Michal Vaskoe2713da2016-08-22 16:06:40 +02001589 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001590 if (!server_opts.binds) {
1591 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001592 ret = -1;
1593 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001594 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001595
Michal Vasko2e6defd2016-10-07 15:48:15 +02001596 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1597 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1598 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001599 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001600
1601 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001602#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001603 case NC_TI_LIBSSH:
1604 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1605 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1606 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001607 ret = -1;
1608 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001609 }
1610 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1611 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1612 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1613 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1614 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001615#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001616#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001617 case NC_TI_OPENSSL:
1618 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1619 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1620 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001621 ret = -1;
1622 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001623 }
1624 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001625#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001626 default:
1627 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001628 ret = -1;
1629 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001630 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001631
Michal Vaskoade892d2017-02-22 13:40:35 +01001632cleanup:
1633 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001634 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001635
Michal Vaskoade892d2017-02-22 13:40:35 +01001636 /* BIND UNLOCK */
1637 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001638
Michal Vaskoade892d2017-02-22 13:40:35 +01001639 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001640}
1641
Michal Vasko3031aae2016-01-27 16:07:18 +01001642int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001643nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001644{
1645 struct nc_endpt *endpt;
1646 struct nc_bind *bind = NULL;
1647 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001648 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001649
Michal Vasko45e53ae2016-04-07 11:46:03 +02001650 if (!endpt_name) {
1651 ERRARG("endpt_name");
1652 return -1;
1653 } else if ((!address && !port) || (address && port)) {
1654 ERRARG("address and port");
1655 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001656 }
1657
Michal Vaskoe2713da2016-08-22 16:06:40 +02001658 if (address) {
1659 set_addr = 1;
1660 } else {
1661 set_addr = 0;
1662 }
1663
Michal Vaskoade892d2017-02-22 13:40:35 +01001664 /* BIND LOCK */
1665 pthread_mutex_lock(&server_opts.bind_lock);
1666
1667 /* ENDPT LOCK */
1668 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001669 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001670 /* BIND UNLOCK */
1671 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001672 return -1;
1673 }
1674
Michal Vaskoe2713da2016-08-22 16:06:40 +02001675 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001676
Michal Vaskoe2713da2016-08-22 16:06:40 +02001677 if (set_addr) {
1678 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001679 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001680 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001681 }
1682
Michal Vaskoe2713da2016-08-22 16:06:40 +02001683 /* we have all the information we need to create a listening socket */
1684 if (address && port) {
1685 /* create new socket, close the old one */
1686 sock = nc_sock_listen(address, port);
1687 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001688 ret = -1;
1689 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001690 }
1691
1692 if (bind->sock > -1) {
1693 close(bind->sock);
1694 }
1695 bind->sock = sock;
1696 } /* else we are just setting address or port */
1697
1698 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001699 lydict_remove(server_opts.ctx, bind->address);
1700 bind->address = lydict_insert(server_opts.ctx, address, 0);
1701 } else {
1702 bind->port = port;
1703 }
1704
Michal Vaskoe2713da2016-08-22 16:06:40 +02001705 if (sock > -1) {
1706#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001707 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001708#elif defined(NC_ENABLED_SSH)
1709 VRB("Listening on %s:%u for SSH connections.", address, port);
1710#else
1711 VRB("Listening on %s:%u for TLS connections.", address, port);
1712#endif
1713 }
1714
Michal Vasko4c1fb492017-01-30 14:31:07 +01001715cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001716 /* ENDPT UNLOCK */
1717 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001718
Michal Vaskoade892d2017-02-22 13:40:35 +01001719 /* BIND UNLOCK */
1720 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001721
Michal Vasko4c1fb492017-01-30 14:31:07 +01001722 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001723}
1724
Michal Vaskoe2713da2016-08-22 16:06:40 +02001725API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001726nc_server_endpt_set_address(const char *endpt_name, const char *address)
1727{
1728 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1729}
1730
1731API int
1732nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1733{
1734 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1735}
1736
1737API int
Michal Vasko59050372016-11-22 14:33:55 +01001738nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001739{
1740 uint32_t i;
1741 int ret = -1;
1742
Michal Vaskoade892d2017-02-22 13:40:35 +01001743 /* BIND LOCK */
1744 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001745
Michal Vaskoade892d2017-02-22 13:40:35 +01001746 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001747 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001748
Michal Vasko59050372016-11-22 14:33:55 +01001749 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001750 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001751 for (i = 0; i < server_opts.endpt_count; ++i) {
1752 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001753 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001754#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001755 case NC_TI_LIBSSH:
1756 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1757 free(server_opts.endpts[i].opts.ssh);
1758 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001759#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001760#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001761 case NC_TI_OPENSSL:
1762 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1763 free(server_opts.endpts[i].opts.tls);
1764 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001765#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001766 default:
1767 ERRINT;
1768 /* won't get here ...*/
1769 break;
1770 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001771 ret = 0;
1772 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001773 free(server_opts.endpts);
1774 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001775
1776 /* remove all binds */
1777 for (i = 0; i < server_opts.endpt_count; ++i) {
1778 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1779 if (server_opts.binds[i].sock > -1) {
1780 close(server_opts.binds[i].sock);
1781 }
1782 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001783 free(server_opts.binds);
1784 server_opts.binds = NULL;
1785
Michal Vasko3031aae2016-01-27 16:07:18 +01001786 server_opts.endpt_count = 0;
1787
Michal Vasko1a38c862016-01-15 15:50:07 +01001788 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001789 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001790 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001791 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001792 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001793 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001794 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001795#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001796 case NC_TI_LIBSSH:
1797 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1798 free(server_opts.endpts[i].opts.ssh);
1799 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001800#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001801#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001802 case NC_TI_OPENSSL:
1803 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1804 free(server_opts.endpts[i].opts.tls);
1805 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001806#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001807 default:
1808 ERRINT;
1809 break;
1810 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001811
Michal Vaskoe2713da2016-08-22 16:06:40 +02001812 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001813 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1814 if (server_opts.binds[i].sock > -1) {
1815 close(server_opts.binds[i].sock);
1816 }
1817
1818 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001819 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001820 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001821 free(server_opts.binds);
1822 server_opts.binds = NULL;
1823 free(server_opts.endpts);
1824 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001825 } else if (i < server_opts.endpt_count) {
1826 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1827 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001828 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001829
1830 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001831 if (name) {
1832 break;
1833 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001834 }
1835 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001836 }
1837
Michal Vaskoade892d2017-02-22 13:40:35 +01001838 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001839 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001840
Michal Vaskoade892d2017-02-22 13:40:35 +01001841 /* BIND UNLOCK */
1842 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001843
1844 return ret;
1845}
1846
Michal Vasko71090fc2016-05-24 16:37:28 +02001847API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001848nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001849{
Michal Vasko71090fc2016-05-24 16:37:28 +02001850 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001851 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001852 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001853 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02001854 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01001855
Michal Vasko45e53ae2016-04-07 11:46:03 +02001856 if (!server_opts.ctx) {
1857 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001858 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001859 } else if (!session) {
1860 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001861 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001862 }
1863
Michal Vaskoade892d2017-02-22 13:40:35 +01001864 /* BIND LOCK */
1865 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001866
1867 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001868 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01001869 /* BIND UNLOCK */
1870 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001871 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001872 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001873
Michal Vaskoe2713da2016-08-22 16:06:40 +02001874 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001875 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001876 /* BIND UNLOCK */
1877 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001878 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001879 if (!ret) {
1880 return NC_MSG_WOULDBLOCK;
1881 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001882 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001883 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001884
1885 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
1886 /* ENDPT READ LOCK */
1887 pthread_rwlock_rdlock(&server_opts.endpt_lock);
1888
1889 /* BIND UNLOCK */
1890 pthread_mutex_unlock(&server_opts.bind_lock);
1891
Michal Vaskob48aa812016-01-18 14:13:09 +01001892 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001893
Michal Vaskoade892d2017-02-22 13:40:35 +01001894 *session = nc_new_session(0);
Michal Vasko686aa312016-01-21 15:58:18 +01001895 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001896 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001897 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001898 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001899 msgtype = NC_MSG_ERROR;
1900 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001901 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001902 (*session)->status = NC_STATUS_STARTING;
1903 (*session)->side = NC_SERVER;
1904 (*session)->ctx = server_opts.ctx;
1905 (*session)->flags = NC_SESSION_SHAREDCTX;
1906 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1907 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001908
1909 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001910 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001911 pthread_cond_init((*session)->ti_cond, NULL);
1912 *(*session)->ti_inuse = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001913
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001914 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001915#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001916 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1917 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001918 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001919 if (ret < 0) {
1920 msgtype = NC_MSG_ERROR;
1921 goto cleanup;
1922 } else if (!ret) {
1923 msgtype = NC_MSG_WOULDBLOCK;
1924 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001925 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001926 } else
1927#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001928#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001929 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1930 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001931 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001932 if (ret < 0) {
1933 msgtype = NC_MSG_ERROR;
1934 goto cleanup;
1935 } else if (!ret) {
1936 msgtype = NC_MSG_WOULDBLOCK;
1937 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001938 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001939 } else
1940#endif
1941 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001942 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001943 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001944 msgtype = NC_MSG_ERROR;
1945 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001946 }
1947
Michal Vasko2cc4c682016-03-01 09:16:48 +01001948 (*session)->data = NULL;
1949
Michal Vaskoade892d2017-02-22 13:40:35 +01001950 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001951 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001952
Michal Vaskob48aa812016-01-18 14:13:09 +01001953 /* assign new SID atomically */
1954 /* LOCK */
1955 pthread_spin_lock(&server_opts.sid_lock);
1956 (*session)->id = server_opts.new_session_id++;
1957 /* UNLOCK */
1958 pthread_spin_unlock(&server_opts.sid_lock);
1959
Michal Vasko9e036d52016-01-08 10:49:26 +01001960 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001961 msgtype = nc_handshake(*session);
1962 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001963 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001964 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001965 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001966 }
Michal Vasko9fb42272017-10-05 13:50:05 +02001967
1968 nc_gettimespec_mono(&ts_cur);
1969 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
1970 nc_gettimespec_real(&ts_cur);
1971 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01001972 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001973
Michal Vasko71090fc2016-05-24 16:37:28 +02001974 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001975
Michal Vasko71090fc2016-05-24 16:37:28 +02001976cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001977 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001978 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001979
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001980 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001981 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001982 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001983}
1984
Michal Vasko2e6defd2016-10-07 15:48:15 +02001985API int
1986nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1987{
1988 uint16_t i;
1989
1990 if (!name) {
1991 ERRARG("name");
1992 return -1;
1993 } else if (!ti) {
1994 ERRARG("ti");
1995 return -1;
1996 }
1997
1998 /* WRITE LOCK */
1999 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2000
2001 /* check name uniqueness */
2002 for (i = 0; i < server_opts.ch_client_count; ++i) {
2003 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2004 ERR("Call Home client \"%s\" already exists.", name);
2005 /* WRITE UNLOCK */
2006 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2007 return -1;
2008 }
2009 }
2010
2011 ++server_opts.ch_client_count;
2012 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2013 if (!server_opts.ch_clients) {
2014 ERRMEM;
2015 /* WRITE UNLOCK */
2016 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2017 return -1;
2018 }
2019 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
2020 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002021 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
2022 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002023
2024 switch (ti) {
2025#ifdef NC_ENABLED_SSH
2026 case NC_TI_LIBSSH:
2027 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2028 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
2029 ERRMEM;
2030 /* WRITE UNLOCK */
2031 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2032 return -1;
2033 }
2034 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
2035 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2036 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
2037 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
2038 break;
2039#endif
2040#ifdef NC_ENABLED_TLS
2041 case NC_TI_OPENSSL:
2042 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2043 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
2044 ERRMEM;
2045 /* WRITE UNLOCK */
2046 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2047 return -1;
2048 }
2049 break;
2050#endif
2051 default:
2052 ERRINT;
2053 /* WRITE UNLOCK */
2054 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2055 return -1;
2056 }
2057
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002058 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
2059
Michal Vasko2e6defd2016-10-07 15:48:15 +02002060 /* set CH default options */
2061 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
2062 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
2063
2064 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
2065
2066 /* WRITE UNLOCK */
2067 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2068
2069 return 0;
2070}
2071
2072API int
Michal Vasko59050372016-11-22 14:33:55 +01002073nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002074{
2075 uint16_t i, j;
2076 int ret = -1;
2077
2078 /* WRITE LOCK */
2079 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2080
Michal Vasko59050372016-11-22 14:33:55 +01002081 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002082 /* remove all CH clients */
2083 for (i = 0; i < server_opts.ch_client_count; ++i) {
2084 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2085
2086 /* remove all endpoints */
2087 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2088 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2089 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2090 }
2091 free(server_opts.ch_clients[i].ch_endpts);
2092
2093 switch (server_opts.ch_clients[i].ti) {
2094#ifdef NC_ENABLED_SSH
2095 case NC_TI_LIBSSH:
2096 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2097 free(server_opts.ch_clients[i].opts.ssh);
2098 break;
2099#endif
2100#ifdef NC_ENABLED_TLS
2101 case NC_TI_OPENSSL:
2102 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2103 free(server_opts.ch_clients[i].opts.tls);
2104 break;
2105#endif
2106 default:
2107 ERRINT;
2108 /* won't get here ...*/
2109 break;
2110 }
2111
2112 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2113
2114 ret = 0;
2115 }
2116 free(server_opts.ch_clients);
2117 server_opts.ch_clients = NULL;
2118
2119 server_opts.ch_client_count = 0;
2120
2121 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002122 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002123 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002124 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 +02002125 /* remove endpt */
2126 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2127
2128 switch (server_opts.ch_clients[i].ti) {
2129#ifdef NC_ENABLED_SSH
2130 case NC_TI_LIBSSH:
2131 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2132 free(server_opts.ch_clients[i].opts.ssh);
2133 break;
2134#endif
2135#ifdef NC_ENABLED_TLS
2136 case NC_TI_OPENSSL:
2137 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2138 free(server_opts.ch_clients[i].opts.tls);
2139 break;
2140#endif
2141 default:
2142 ERRINT;
2143 break;
2144 }
2145
2146 /* remove all endpoints */
2147 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2148 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2149 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2150 }
2151 free(server_opts.ch_clients[i].ch_endpts);
2152
2153 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2154
2155 /* move last client and endpoint(s) to the empty space */
2156 --server_opts.ch_client_count;
2157 if (i < server_opts.ch_client_count) {
2158 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2159 sizeof *server_opts.ch_clients);
2160 } else if (!server_opts.ch_client_count) {
2161 free(server_opts.ch_clients);
2162 server_opts.ch_clients = NULL;
2163 }
2164
2165 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002166 if (name) {
2167 break;
2168 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002169 }
2170 }
2171 }
2172
2173 /* WRITE UNLOCK */
2174 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2175
2176 return ret;
2177}
2178
2179API int
2180nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2181{
2182 uint16_t i;
2183 struct nc_ch_client *client;
2184
2185 if (!client_name) {
2186 ERRARG("client_name");
2187 return -1;
2188 } else if (!endpt_name) {
2189 ERRARG("endpt_name");
2190 return -1;
2191 }
2192
2193 /* LOCK */
2194 client = nc_server_ch_client_lock(client_name, 0, NULL);
2195 if (!client) {
2196 return -1;
2197 }
2198
2199 for (i = 0; i < client->ch_endpt_count; ++i) {
2200 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2201 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2202 /* UNLOCK */
2203 nc_server_ch_client_unlock(client);
2204 return -1;
2205 }
2206 }
2207
2208 ++client->ch_endpt_count;
2209 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2210 if (!client->ch_endpts) {
2211 ERRMEM;
2212 /* UNLOCK */
2213 nc_server_ch_client_unlock(client);
2214 return -1;
2215 }
2216
2217 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2218 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2219 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
2220
2221 /* UNLOCK */
2222 nc_server_ch_client_unlock(client);
2223
2224 return 0;
2225}
2226
2227API int
2228nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2229{
2230 uint16_t i;
2231 int ret = -1;
2232 struct nc_ch_client *client;
2233
2234 if (!client_name) {
2235 ERRARG("client_name");
2236 return -1;
2237 }
2238
2239 /* LOCK */
2240 client = nc_server_ch_client_lock(client_name, 0, NULL);
2241 if (!client) {
2242 return -1;
2243 }
2244
2245 if (!endpt_name) {
2246 /* remove all endpoints */
2247 for (i = 0; i < client->ch_endpt_count; ++i) {
2248 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2249 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2250 }
2251 free(client->ch_endpts);
2252 client->ch_endpts = NULL;
2253 client->ch_endpt_count = 0;
2254
2255 ret = 0;
2256 } else {
2257 for (i = 0; i < client->ch_endpt_count; ++i) {
2258 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2259 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2260 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002261
Michal Vasko4f921012016-10-20 14:07:45 +02002262 /* move last endpoint to the empty space */
2263 --client->ch_endpt_count;
2264 if (i < client->ch_endpt_count) {
2265 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2266 } else if (!server_opts.ch_client_count) {
2267 free(server_opts.ch_clients);
2268 server_opts.ch_clients = NULL;
2269 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002270
Michal Vasko4f921012016-10-20 14:07:45 +02002271 ret = 0;
2272 break;
2273 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002274 }
2275 }
2276
2277 /* UNLOCK */
2278 nc_server_ch_client_unlock(client);
2279
2280 return ret;
2281}
2282
2283API int
2284nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2285{
2286 uint16_t i;
2287 int ret = -1;
2288 struct nc_ch_client *client;
2289
2290 if (!client_name) {
2291 ERRARG("client_name");
2292 return -1;
2293 } else if (!endpt_name) {
2294 ERRARG("endpt_name");
2295 return -1;
2296 } else if (!address) {
2297 ERRARG("address");
2298 return -1;
2299 }
2300
2301 /* LOCK */
2302 client = nc_server_ch_client_lock(client_name, 0, NULL);
2303 if (!client) {
2304 return -1;
2305 }
2306
2307 for (i = 0; i < client->ch_endpt_count; ++i) {
2308 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2309 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2310 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2311
2312 ret = 0;
2313 break;
2314 }
2315 }
2316
2317 /* UNLOCK */
2318 nc_server_ch_client_unlock(client);
2319
2320 if (ret == -1) {
2321 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2322 }
2323
2324 return ret;
2325}
2326
2327API int
2328nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2329{
2330 uint16_t i;
2331 int ret = -1;
2332 struct nc_ch_client *client;
2333
2334 if (!client_name) {
2335 ERRARG("client_name");
2336 return -1;
2337 } else if (!endpt_name) {
2338 ERRARG("endpt_name");
2339 return -1;
2340 } else if (!port) {
2341 ERRARG("port");
2342 return -1;
2343 }
2344
2345 /* LOCK */
2346 client = nc_server_ch_client_lock(client_name, 0, NULL);
2347 if (!client) {
2348 return -1;
2349 }
2350
2351 for (i = 0; i < client->ch_endpt_count; ++i) {
2352 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2353 client->ch_endpts[i].port = port;
2354
2355 ret = 0;
2356 break;
2357 }
2358 }
2359
2360 /* UNLOCK */
2361 nc_server_ch_client_unlock(client);
2362
2363 if (ret == -1) {
2364 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2365 }
2366
2367 return ret;
2368}
2369
2370API int
2371nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2372{
2373 struct nc_ch_client *client;
2374
2375 if (!client_name) {
2376 ERRARG("client_name");
2377 return -1;
2378 } else if (!conn_type) {
2379 ERRARG("conn_type");
2380 return -1;
2381 }
2382
2383 /* LOCK */
2384 client = nc_server_ch_client_lock(client_name, 0, NULL);
2385 if (!client) {
2386 return -1;
2387 }
2388
2389 if (client->conn_type != conn_type) {
2390 client->conn_type = conn_type;
2391
2392 /* set default options */
2393 switch (conn_type) {
2394 case NC_CH_PERSIST:
2395 client->conn.persist.idle_timeout = 86400;
2396 client->conn.persist.ka_max_wait = 30;
2397 client->conn.persist.ka_max_attempts = 3;
2398 break;
2399 case NC_CH_PERIOD:
2400 client->conn.period.idle_timeout = 300;
2401 client->conn.period.reconnect_timeout = 60;
2402 break;
2403 default:
2404 ERRINT;
2405 break;
2406 }
2407 }
2408
2409 /* UNLOCK */
2410 nc_server_ch_client_unlock(client);
2411
2412 return 0;
2413}
2414
2415API int
2416nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2417{
2418 struct nc_ch_client *client;
2419
2420 if (!client_name) {
2421 ERRARG("client_name");
2422 return -1;
2423 }
2424
2425 /* LOCK */
2426 client = nc_server_ch_client_lock(client_name, 0, NULL);
2427 if (!client) {
2428 return -1;
2429 }
2430
2431 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002432 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002433 /* UNLOCK */
2434 nc_server_ch_client_unlock(client);
2435 return -1;
2436 }
2437
2438 client->conn.persist.idle_timeout = idle_timeout;
2439
2440 /* UNLOCK */
2441 nc_server_ch_client_unlock(client);
2442
2443 return 0;
2444}
2445
2446API int
2447nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2448{
2449 struct nc_ch_client *client;
2450
2451 if (!client_name) {
2452 ERRARG("client_name");
2453 return -1;
2454 } else if (!max_wait) {
2455 ERRARG("max_wait");
2456 return -1;
2457 }
2458
2459 /* LOCK */
2460 client = nc_server_ch_client_lock(client_name, 0, NULL);
2461 if (!client) {
2462 return -1;
2463 }
2464
2465 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002466 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002467 /* UNLOCK */
2468 nc_server_ch_client_unlock(client);
2469 return -1;
2470 }
2471
2472 client->conn.persist.ka_max_wait = max_wait;
2473
2474 /* UNLOCK */
2475 nc_server_ch_client_unlock(client);
2476
2477 return 0;
2478}
2479
2480API int
2481nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2482{
2483 struct nc_ch_client *client;
2484
2485 if (!client_name) {
2486 ERRARG("client_name");
2487 return -1;
2488 }
2489
2490 /* LOCK */
2491 client = nc_server_ch_client_lock(client_name, 0, NULL);
2492 if (!client) {
2493 return -1;
2494 }
2495
2496 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002497 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002498 /* UNLOCK */
2499 nc_server_ch_client_unlock(client);
2500 return -1;
2501 }
2502
2503 client->conn.persist.ka_max_attempts = max_attempts;
2504
2505 /* UNLOCK */
2506 nc_server_ch_client_unlock(client);
2507
2508 return 0;
2509}
2510
2511API int
2512nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2513{
2514 struct nc_ch_client *client;
2515
2516 if (!client_name) {
2517 ERRARG("client_name");
2518 return -1;
2519 }
2520
2521 /* LOCK */
2522 client = nc_server_ch_client_lock(client_name, 0, NULL);
2523 if (!client) {
2524 return -1;
2525 }
2526
2527 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002528 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002529 /* UNLOCK */
2530 nc_server_ch_client_unlock(client);
2531 return -1;
2532 }
2533
2534 client->conn.period.idle_timeout = idle_timeout;
2535
2536 /* UNLOCK */
2537 nc_server_ch_client_unlock(client);
2538
2539 return 0;
2540}
2541
2542API int
2543nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2544{
2545 struct nc_ch_client *client;
2546
2547 if (!client_name) {
2548 ERRARG("client_name");
2549 return -1;
2550 } else if (!reconnect_timeout) {
2551 ERRARG("reconnect_timeout");
2552 return -1;
2553 }
2554
2555 /* LOCK */
2556 client = nc_server_ch_client_lock(client_name, 0, NULL);
2557 if (!client) {
2558 return -1;
2559 }
2560
2561 if (client->conn_type != NC_CH_PERIOD) {
2562 ERR("Call Home client \"%s\" is not of periodic connection type.");
2563 /* UNLOCK */
2564 nc_server_ch_client_unlock(client);
2565 return -1;
2566 }
2567
2568 client->conn.period.reconnect_timeout = reconnect_timeout;
2569
2570 /* UNLOCK */
2571 nc_server_ch_client_unlock(client);
2572
2573 return 0;
2574}
2575
2576API int
2577nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2578{
2579 struct nc_ch_client *client;
2580
2581 if (!client_name) {
2582 ERRARG("client_name");
2583 return -1;
2584 }
2585
2586 /* LOCK */
2587 client = nc_server_ch_client_lock(client_name, 0, NULL);
2588 if (!client) {
2589 return -1;
2590 }
2591
2592 client->start_with = start_with;
2593
2594 /* UNLOCK */
2595 nc_server_ch_client_unlock(client);
2596
2597 return 0;
2598}
2599
2600API int
2601nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2602{
2603 struct nc_ch_client *client;
2604
2605 if (!client_name) {
2606 ERRARG("client_name");
2607 return -1;
2608 } else if (!max_attempts) {
2609 ERRARG("max_attempts");
2610 return -1;
2611 }
2612
2613 /* LOCK */
2614 client = nc_server_ch_client_lock(client_name, 0, NULL);
2615 if (!client) {
2616 return -1;
2617 }
2618
2619 client->max_attempts = max_attempts;
2620
2621 /* UNLOCK */
2622 nc_server_ch_client_unlock(client);
2623
2624 return 0;
2625}
2626
2627/* client lock is expected to be held */
2628static NC_MSG_TYPE
2629nc_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 +01002630{
Michal Vasko71090fc2016-05-24 16:37:28 +02002631 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002632 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002633 struct timespec ts_cur;
Michal Vaskob05053d2016-01-22 16:12:06 +01002634
Michal Vasko2e6defd2016-10-07 15:48:15 +02002635 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002636 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002637 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002638 }
2639
Michal Vaskoade892d2017-02-22 13:40:35 +01002640 *session = nc_new_session(0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002641 if (!(*session)) {
2642 ERRMEM;
2643 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002644 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002645 }
2646 (*session)->status = NC_STATUS_STARTING;
2647 (*session)->side = NC_SERVER;
2648 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002649 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002650 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2651 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002652
2653 /* transport lock */
Michal Vaskob05053d2016-01-22 16:12:06 +01002654 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01002655 pthread_cond_init((*session)->ti_cond, NULL);
2656 *(*session)->ti_inuse = 0;
Michal Vaskob05053d2016-01-22 16:12:06 +01002657
2658 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002659#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002660 if (client->ti == NC_TI_LIBSSH) {
2661 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002662 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002663 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002664
Michal Vasko71090fc2016-05-24 16:37:28 +02002665 if (ret < 0) {
2666 msgtype = NC_MSG_ERROR;
2667 goto fail;
2668 } else if (!ret) {
2669 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002670 goto fail;
2671 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002672 } else
2673#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002674#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002675 if (client->ti == NC_TI_OPENSSL) {
2676 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002677 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002678 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002679
Michal Vasko71090fc2016-05-24 16:37:28 +02002680 if (ret < 0) {
2681 msgtype = NC_MSG_ERROR;
2682 goto fail;
2683 } else if (!ret) {
2684 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002685 goto fail;
2686 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002687 } else
2688#endif
2689 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002690 ERRINT;
2691 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002692 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002693 goto fail;
2694 }
2695
2696 /* assign new SID atomically */
2697 /* LOCK */
2698 pthread_spin_lock(&server_opts.sid_lock);
2699 (*session)->id = server_opts.new_session_id++;
2700 /* UNLOCK */
2701 pthread_spin_unlock(&server_opts.sid_lock);
2702
2703 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002704 msgtype = nc_handshake(*session);
2705 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002706 goto fail;
2707 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002708
2709 nc_gettimespec_mono(&ts_cur);
2710 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2711 nc_gettimespec_real(&ts_cur);
2712 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01002713 (*session)->status = NC_STATUS_RUNNING;
2714
Michal Vasko71090fc2016-05-24 16:37:28 +02002715 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002716
2717fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002718 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002719 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002720 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002721}
2722
Michal Vasko2e6defd2016-10-07 15:48:15 +02002723struct nc_ch_client_thread_arg {
2724 char *client_name;
2725 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2726};
2727
2728static struct nc_ch_client *
2729nc_server_ch_client_with_endpt_lock(const char *name)
2730{
2731 struct nc_ch_client *client;
2732
2733 while (1) {
2734 /* LOCK */
2735 client = nc_server_ch_client_lock(name, 0, NULL);
2736 if (!client) {
2737 return NULL;
2738 }
2739 if (client->ch_endpt_count) {
2740 return client;
2741 }
2742 /* no endpoints defined yet */
2743
2744 /* UNLOCK */
2745 nc_server_ch_client_unlock(client);
2746
2747 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2748 }
2749
2750 return NULL;
2751}
2752
2753static int
2754nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2755{
2756 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002757 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002758 struct timespec ts;
2759 struct nc_ch_client *client;
2760
2761 /* session created, initialize condition */
2762 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2763 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2764 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2765 ERRMEM;
2766 nc_session_free(session, NULL);
2767 return -1;
2768 }
2769 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2770 pthread_cond_init(session->opts.server.ch_cond, NULL);
2771
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002772 session->flags |= NC_SESSION_CALLHOME;
2773
Michal Vasko2e6defd2016-10-07 15:48:15 +02002774 /* CH LOCK */
2775 pthread_mutex_lock(session->opts.server.ch_lock);
2776
2777 /* give the session to the user */
2778 data->session_clb(data->client_name, session);
2779
2780 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02002781 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01002782 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002783
2784 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2785 if (ret && (ret != ETIMEDOUT)) {
2786 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2787 goto ch_client_remove;
2788 }
2789
2790 /* check whether the client was not removed */
2791 /* LOCK */
2792 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2793 if (!client) {
2794 /* client was removed, finish thread */
2795 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2796 data->client_name);
2797 goto ch_client_remove;
2798 }
2799
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002800 if (client->conn_type == NC_CH_PERSIST) {
2801 /* TODO keep-alives */
2802 idle_timeout = client->conn.persist.idle_timeout;
2803 } else {
2804 idle_timeout = client->conn.period.idle_timeout;
2805 }
2806
Michal Vasko9fb42272017-10-05 13:50:05 +02002807 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01002808 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 +02002809 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2810 session->status = NC_STATUS_INVALID;
2811 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2812 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002813
2814 /* UNLOCK */
2815 nc_server_ch_client_unlock(client);
2816
2817 } while (session->status == NC_STATUS_RUNNING);
2818
2819 /* CH UNLOCK */
2820 pthread_mutex_unlock(session->opts.server.ch_lock);
2821
2822 return 0;
2823
2824ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002825 /* make the session a standard one */
2826 pthread_cond_destroy(session->opts.server.ch_cond);
2827 free(session->opts.server.ch_cond);
2828 session->opts.server.ch_cond = NULL;
2829
2830 session->flags &= ~NC_SESSION_CALLHOME;
2831
Michal Vasko2e6defd2016-10-07 15:48:15 +02002832 /* CH UNLOCK */
2833 pthread_mutex_unlock(session->opts.server.ch_lock);
2834
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002835 pthread_mutex_destroy(session->opts.server.ch_lock);
2836 free(session->opts.server.ch_lock);
2837 session->opts.server.ch_lock = NULL;
2838
Michal Vasko2e6defd2016-10-07 15:48:15 +02002839 return 1;
2840}
2841
2842static void *
2843nc_ch_client_thread(void *arg)
2844{
2845 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2846 NC_MSG_TYPE msgtype;
2847 uint8_t cur_attempts = 0;
2848 uint16_t i;
Michal Vasko9550cf12017-03-21 15:33:58 +01002849 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002850 struct nc_ch_endpt *cur_endpt;
2851 struct nc_session *session;
2852 struct nc_ch_client *client;
2853
2854 /* LOCK */
2855 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2856 if (!client) {
2857 goto cleanup;
2858 }
2859
2860 cur_endpt = &client->ch_endpts[0];
2861 cur_endpt_name = strdup(cur_endpt->name);
2862
Michal Vasko29af44b2016-10-13 10:59:55 +02002863 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002864 while (1) {
2865 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2866
2867 if (msgtype == NC_MSG_HELLO) {
2868 /* UNLOCK */
2869 nc_server_ch_client_unlock(client);
2870
Michal Vasko29af44b2016-10-13 10:59:55 +02002871 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002872 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2873 goto cleanup;
2874 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002875 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002876
2877 /* LOCK */
2878 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2879 if (!client) {
2880 goto cleanup;
2881 }
2882
2883 /* session changed status -> it was disconnected for whatever reason,
2884 * persistent connection immediately tries to reconnect, periodic waits some first */
2885 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002886 /* UNLOCK */
2887 nc_server_ch_client_unlock(client);
2888
2889 /* TODO wake up sometimes to check for new notifications */
2890 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2891
2892 /* LOCK */
2893 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2894 if (!client) {
2895 goto cleanup;
2896 }
2897 }
2898
2899 /* set next endpoint to try */
2900 if (client->start_with == NC_CH_FIRST_LISTED) {
2901 cur_endpt = &client->ch_endpts[0];
2902 free(cur_endpt_name);
2903 cur_endpt_name = strdup(cur_endpt->name);
2904 } /* else we keep the current one */
2905 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02002906 /* UNLOCK */
2907 nc_server_ch_client_unlock(client);
2908
Michal Vasko2e6defd2016-10-07 15:48:15 +02002909 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002910 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2911
Michal Vasko6bb116b2016-10-26 13:53:46 +02002912 /* LOCK */
2913 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2914 if (!client) {
2915 goto cleanup;
2916 }
2917
Michal Vasko2e6defd2016-10-07 15:48:15 +02002918 ++cur_attempts;
2919 if (cur_attempts == client->max_attempts) {
2920 for (i = 0; i < client->ch_endpt_count; ++i) {
2921 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2922 break;
2923 }
2924 }
2925 if (i < client->ch_endpt_count - 1) {
2926 /* just go to the next endpoint */
2927 cur_endpt = &client->ch_endpts[i + 1];
2928 free(cur_endpt_name);
2929 cur_endpt_name = strdup(cur_endpt->name);
2930 } else {
2931 /* cur_endpoint was removed or is the last, either way start with the first one */
2932 cur_endpt = &client->ch_endpts[0];
2933 free(cur_endpt_name);
2934 cur_endpt_name = strdup(cur_endpt->name);
2935 }
2936
2937 cur_attempts = 0;
2938 } /* else we keep the current one */
2939 }
2940 }
2941
2942cleanup:
2943 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01002944 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002945 free(data->client_name);
2946 free(data);
2947 return NULL;
2948}
2949
2950API int
2951nc_connect_ch_client_dispatch(const char *client_name,
2952 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2953 int ret;
2954 pthread_t tid;
2955 struct nc_ch_client_thread_arg *arg;
2956
2957 if (!client_name) {
2958 ERRARG("client_name");
2959 return -1;
2960 } else if (!session_clb) {
2961 ERRARG("session_clb");
2962 return -1;
2963 }
2964
2965 arg = malloc(sizeof *arg);
2966 if (!arg) {
2967 ERRMEM;
2968 return -1;
2969 }
2970 arg->client_name = strdup(client_name);
2971 if (!arg->client_name) {
2972 ERRMEM;
2973 free(arg);
2974 return -1;
2975 }
2976 arg->session_clb = session_clb;
2977
2978 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2979 if (ret) {
2980 ERR("Creating a new thread failed (%s).", strerror(ret));
2981 free(arg->client_name);
2982 free(arg);
2983 return -1;
2984 }
2985 /* the thread now manages arg */
2986
2987 pthread_detach(tid);
2988
2989 return 0;
2990}
2991
Radek Krejci53691be2016-02-22 13:58:37 +01002992#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002993
Michal Vaskoe8e07702017-03-15 10:19:30 +01002994API int
2995nc_server_endpt_count(void)
2996{
2997 return server_opts.endpt_count;
2998}
2999
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003000API time_t
3001nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003002{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003003 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003004 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003005 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003006 }
3007
Michal Vasko2e6defd2016-10-07 15:48:15 +02003008 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003009}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003010
3011API void
3012nc_session_set_notif_status(struct nc_session *session, int notif_status)
3013{
3014 if (!session || (session->side != NC_SERVER)) {
3015 ERRARG("session");
3016 return;
3017 }
3018
3019 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3020}
3021
3022API int
3023nc_session_get_notif_status(const struct nc_session *session)
3024{
3025 if (!session || (session->side != NC_SERVER)) {
3026 ERRARG("session");
3027 return 0;
3028 }
3029
3030 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003031}