blob: 672e4c9b5e9cf893b1e3b8ab1e7d72e468bef449 [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 Vasko05ba9df2016-01-13 14:40:27 +0100420 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
Radek Krejci90fba642016-12-07 15:59:45 +0100421 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100422 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
423 } 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
670nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
671{
672 uint8_t i, found = 0;
673
674 for (i = 0; i < ps->queue_len; ++i) {
675 /* idx round buffer adjust */
676 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
677 i = -ps->queue_begin;
678 }
679
680 if (found) {
681 /* move the value back one place */
682 if (ps->queue[ps->queue_begin + i] == id) {
683 /* another equal value, simply cannot be */
684 ERRINT;
685 }
686
687 if (ps->queue_begin + i == 0) {
688 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
689 } else {
690 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
691 }
692 } else if (ps->queue[ps->queue_begin + i] == id) {
693 /* found our id, there can be no more equal valid values */
694 found = 1;
695 }
696 }
697
698 if (!found) {
699 ERRINT;
700 }
701 --ps->queue_len;
702}
703
Michal Vaskof04a52a2016-04-07 10:52:10 +0200704int
Michal Vasko26043172016-07-26 14:08:59 +0200705nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200706{
707 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200708 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200709 struct timespec ts;
710
Michal Vasko77a6abe2017-10-05 10:02:20 +0200711 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100712 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200713
714 /* LOCK */
715 ret = pthread_mutex_timedlock(&ps->lock, &ts);
716 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200717 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200718 return -1;
719 }
720
721 /* get a unique queue value (by adding 1 to the last added value, if any) */
722 if (ps->queue_len) {
723 queue_last = ps->queue_begin + ps->queue_len - 1;
724 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
725 queue_last -= NC_PS_QUEUE_SIZE;
726 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200727 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200728 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200729 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200730 }
731
732 /* add ourselves into the queue */
733 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200734 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200735 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200736 return -1;
737 }
738 ++ps->queue_len;
739 queue_last = ps->queue_begin + ps->queue_len - 1;
740 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
741 queue_last -= NC_PS_QUEUE_SIZE;
742 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200743 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200744
745 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200746 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200747 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100748 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200749
750 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
751 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200752 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200753 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200754 nc_ps_queue_remove_id(ps, *id);
755 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200756 return -1;
757 }
758 }
759
Michal Vaskobe86fe32016-04-07 10:43:03 +0200760 /* UNLOCK */
761 pthread_mutex_unlock(&ps->lock);
762
763 return 0;
764}
765
Michal Vaskof04a52a2016-04-07 10:52:10 +0200766int
Michal Vasko26043172016-07-26 14:08:59 +0200767nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200768{
769 int ret;
770 struct timespec ts;
771
Michal Vasko77a6abe2017-10-05 10:02:20 +0200772 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100773 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200774
775 /* LOCK */
776 ret = pthread_mutex_timedlock(&ps->lock, &ts);
777 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200778 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200779 ret = -1;
780 }
781
Michal Vaskob30b99c2016-07-26 11:35:43 +0200782 /* we must be the first, it was our turn after all, right? */
783 if (ps->queue[ps->queue_begin] != id) {
784 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200785 /* UNLOCK */
786 if (!ret) {
787 pthread_mutex_unlock(&ps->lock);
788 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200789 return -1;
790 }
791
Michal Vaskobe86fe32016-04-07 10:43:03 +0200792 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200793 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200794
795 /* broadcast to all other threads that the queue moved */
796 pthread_cond_broadcast(&ps->cond);
797
Michal Vaskobe86fe32016-04-07 10:43:03 +0200798 /* UNLOCK */
799 if (!ret) {
800 pthread_mutex_unlock(&ps->lock);
801 }
802
803 return ret;
804}
805
Michal Vasko428087d2016-01-14 16:04:28 +0100806API struct nc_pollsession *
807nc_ps_new(void)
808{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100809 struct nc_pollsession *ps;
810
811 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100812 if (!ps) {
813 ERRMEM;
814 return NULL;
815 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200816 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100817 pthread_mutex_init(&ps->lock, NULL);
818
819 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100820}
821
822API void
823nc_ps_free(struct nc_pollsession *ps)
824{
fanchanghu3d4e7212017-08-09 09:42:30 +0800825 uint16_t i;
826
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100827 if (!ps) {
828 return;
829 }
830
Michal Vaskobe86fe32016-04-07 10:43:03 +0200831 if (ps->queue_len) {
832 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
833 }
834
fanchanghu3d4e7212017-08-09 09:42:30 +0800835 for (i = 0; i < ps->session_count; i++) {
836 free(ps->sessions[i]);
837 }
838
Michal Vasko428087d2016-01-14 16:04:28 +0100839 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100840 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200841 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100842
Michal Vasko428087d2016-01-14 16:04:28 +0100843 free(ps);
844}
845
846API int
847nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
848{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200849 uint8_t q_id;
850
Michal Vasko45e53ae2016-04-07 11:46:03 +0200851 if (!ps) {
852 ERRARG("ps");
853 return -1;
854 } else if (!session) {
855 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100856 return -1;
857 }
858
Michal Vasko48a63ed2016-03-01 09:48:21 +0100859 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200860 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200861 return -1;
862 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100863
Michal Vasko428087d2016-01-14 16:04:28 +0100864 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100865 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +0100866 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100867 ERRMEM;
868 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200869 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100870 return -1;
871 }
fanchanghu3d4e7212017-08-09 09:42:30 +0800872 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
873 if (!ps->sessions[ps->session_count - 1]) {
874 ERRMEM;
875 --ps->session_count;
876 /* UNLOCK */
877 nc_ps_unlock(ps, q_id, __func__);
878 return -1;
879 }
880 ps->sessions[ps->session_count - 1]->session = session;
881 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +0100882
Michal Vasko48a63ed2016-03-01 09:48:21 +0100883 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200884 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100885}
886
Michal Vasko48a63ed2016-03-01 09:48:21 +0100887static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100888_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100889{
890 uint16_t i;
891
Radek Krejcid5f978f2016-03-03 13:14:45 +0100892 if (index >= 0) {
893 i = (uint16_t)index;
894 goto remove;
895 }
Michal Vasko428087d2016-01-14 16:04:28 +0100896 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +0800897 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100898remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100899 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +0800900 if (i <= ps->session_count) {
901 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +0100902 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +0800903 }
904 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +0100905 free(ps->sessions);
906 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +0100907 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +0100908 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100909 return 0;
910 }
911 }
912
Michal Vaskof0537d82016-01-29 14:42:38 +0100913 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100914}
915
Michal Vasko48a63ed2016-03-01 09:48:21 +0100916API int
917nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
918{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200919 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200920 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100921
Michal Vasko45e53ae2016-04-07 11:46:03 +0200922 if (!ps) {
923 ERRARG("ps");
924 return -1;
925 } else if (!session) {
926 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100927 return -1;
928 }
929
930 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200931 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200932 return -1;
933 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100934
Radek Krejcid5f978f2016-03-03 13:14:45 +0100935 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100936
937 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200938 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100939
Michal Vaskobe86fe32016-04-07 10:43:03 +0200940 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100941}
942
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100943API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +0200944nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100945{
946 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100947 struct nc_session *ret = NULL;
948
949 if (!ps) {
950 ERRARG("ps");
951 return NULL;
952 }
953
954 /* LOCK */
955 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
956 return NULL;
957 }
958
Michal Vasko4871c9d2017-10-09 14:48:39 +0200959 if (idx < ps->session_count) {
960 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100961 }
962
963 /* UNLOCK */
964 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
965
966 return ret;
967}
968
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100969API uint16_t
970nc_ps_session_count(struct nc_pollsession *ps)
971{
972 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200973 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100974 return 0;
975 }
976
Michal Vaskof4462fd2017-02-15 14:29:05 +0100977 return ps->session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100978}
979
Michal Vasko71090fc2016-05-24 16:37:28 +0200980/* must be called holding the session lock!
981 * returns: NC_PSPOLL_ERROR,
982 * NC_PSPOLL_BAD_RPC,
983 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
984 * NC_PSPOLL_RPC
985 */
986static int
Radek Krejci93e80222016-10-03 13:34:25 +0200987nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100988{
989 struct lyxml_elem *xml = NULL;
990 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200991 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200992 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100993
Michal Vasko45e53ae2016-04-07 11:46:03 +0200994 if (!session) {
995 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200996 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200997 } else if (!rpc) {
998 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200999 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001000 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001001 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001002 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001003 }
1004
1005 msgtype = nc_read_msg(session, &xml);
1006
1007 switch (msgtype) {
1008 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001009 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001010 if (!*rpc) {
1011 ERRMEM;
1012 goto error;
1013 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001014
Radek Krejcif93c7d42016-04-06 13:41:15 +02001015 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001016 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1017 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001018 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001019 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +02001020 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +02001021 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001022 nc_server_reply_free(reply);
1023 if (ret == -1) {
1024 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001025 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001026 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1027 } else {
1028 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001029 }
Michal Vasko428087d2016-01-14 16:04:28 +01001030 (*rpc)->root = xml;
1031 break;
1032 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001033 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001034 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001035 goto error;
1036 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001037 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001038 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001039 goto error;
1040 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001041 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001042 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001043 goto error;
1044 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001045 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +01001046 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
1047 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001048 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001049 break;
1050 }
1051
Michal Vasko71090fc2016-05-24 16:37:28 +02001052 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001053
1054error:
1055 /* cleanup */
1056 lyxml_free(server_opts.ctx, xml);
1057
Michal Vasko71090fc2016-05-24 16:37:28 +02001058 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001059}
1060
fanchanghu966f2de2016-07-21 02:28:57 -04001061API void
1062nc_set_global_rpc_clb(nc_rpc_clb clb)
1063{
1064 global_rpc_clb = clb;
1065}
1066
Radek Krejci93e80222016-10-03 13:34:25 +02001067API NC_MSG_TYPE
1068nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1069{
1070 NC_MSG_TYPE result = NC_MSG_NOTIF;
1071 int ret;
1072
1073 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001074 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001075 ERRARG("session");
1076 return NC_MSG_ERROR;
1077 } else if (!notif || !notif->tree || !notif->eventtime) {
1078 ERRARG("notif");
1079 return NC_MSG_ERROR;
1080 }
1081
1082 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vaskoade892d2017-02-22 13:40:35 +01001083 ret = nc_session_lock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001084 if (ret < 0) {
1085 return NC_MSG_ERROR;
1086 } else if (!ret) {
1087 return NC_MSG_WOULDBLOCK;
1088 }
1089
1090 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1091 if (ret == -1) {
1092 ERR("Session %u: failed to write notification.", session->id);
1093 result = NC_MSG_ERROR;
1094 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001095
1096 nc_session_unlock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001097
1098 return result;
1099}
1100
Michal Vasko71090fc2016-05-24 16:37:28 +02001101/* must be called holding the session lock!
1102 * returns: NC_PSPOLL_ERROR,
1103 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1104 * NC_PSPOLL_REPLY_ERROR,
1105 * 0
1106 */
1107static int
Radek Krejci93e80222016-10-03 13:34:25 +02001108nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001109{
1110 nc_rpc_clb clb;
1111 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001112 struct lys_node *rpc_act = NULL;
1113 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001114 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001115
Michal Vasko4a827e52016-03-03 10:59:00 +01001116 if (!rpc) {
1117 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001118 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001119 }
1120
Michal Vasko90e8e692016-07-13 12:27:57 +02001121 if (rpc->tree->schema->nodetype == LYS_RPC) {
1122 /* RPC */
1123 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001124 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001125 /* action */
1126 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1127 if (elem->schema->nodetype == LYS_ACTION) {
1128 rpc_act = elem->schema;
1129 break;
1130 }
1131 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001132 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001133 if (!rpc_act) {
1134 ERRINT;
1135 return NC_PSPOLL_ERROR;
1136 }
1137 }
1138
1139 if (!rpc_act->priv) {
1140 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001141 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001142 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001143 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001144 reply = clb(rpc->tree, session);
1145 }
1146
1147 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001148 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001149 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001150 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1151 if (reply->type == NC_RPL_ERROR) {
1152 ret |= NC_PSPOLL_REPLY_ERROR;
1153 }
1154 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001155
Michal Vasko71090fc2016-05-24 16:37:28 +02001156 if (r == -1) {
1157 ERR("Session %u: failed to write reply.", session->id);
1158 ret |= NC_PSPOLL_ERROR;
1159 }
Michal Vasko428087d2016-01-14 16:04:28 +01001160
1161 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1162 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1163 session->status = NC_STATUS_INVALID;
1164 }
1165
Michal Vasko71090fc2016-05-24 16:37:28 +02001166 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001167}
1168
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001169/* session must be running and session lock held!
1170 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1171 * NC_PSPOLL_ERROR, (msg filled)
1172 * NC_PSPOLL_TIMEOUT,
1173 * NC_PSPOLL_RPC (some application data available),
1174 * NC_PSPOLL_SSH_CHANNEL,
1175 * NC_PSPOLL_SSH_MSG
1176 */
1177static int
Michal Vasko9fb42272017-10-05 13:50:05 +02001178nc_ps_poll_session(struct nc_session *session, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001179{
Michal Vasko9a327362017-01-11 11:31:46 +01001180 struct pollfd pfd;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001181 int r, ret;
Michal Vasko9a327362017-01-11 11:31:46 +01001182#ifdef NC_ENABLED_SSH
1183 struct nc_session *new;
1184#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001185
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001186 /* check timeout first */
1187 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001188 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001189 sprintf(msg, "session idle timeout elapsed");
1190 session->status = NC_STATUS_INVALID;
1191 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1192 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1193 }
1194
1195 switch (session->ti_type) {
1196#ifdef NC_ENABLED_SSH
1197 case NC_TI_LIBSSH:
1198 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001199 if (r == SSH_EOF) {
1200 sprintf(msg, "SSH channel unexpected EOF");
1201 session->status = NC_STATUS_INVALID;
1202 session->term_reason = NC_SESSION_TERM_DROPPED;
1203 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1204 } else if (r == SSH_ERROR) {
1205 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001206 session->status = NC_STATUS_INVALID;
1207 session->term_reason = NC_SESSION_TERM_OTHER;
1208 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001209 } else if (!r) {
1210 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1211 /* new SSH message */
1212 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1213 if (session->ti.libssh.next) {
1214 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1215 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1216 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1217 /* new NETCONF SSH channel */
1218 ret = NC_PSPOLL_SSH_CHANNEL;
1219 break;
1220 }
1221 }
1222 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001223 break;
1224 }
1225 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001226
Michal Vasko8dcaa882017-10-19 14:28:42 +02001227 /* just some SSH message */
1228 ret = NC_PSPOLL_SSH_MSG;
1229 } else {
1230 ret = NC_PSPOLL_TIMEOUT;
1231 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001232 } else {
1233 /* we have some application data */
1234 ret = NC_PSPOLL_RPC;
1235 }
1236 break;
1237#endif
1238#ifdef NC_ENABLED_TLS
1239 case NC_TI_OPENSSL:
1240 r = SSL_pending(session->ti.tls);
1241 if (!r) {
1242 /* no data pending in the SSL buffer, poll fd */
1243 pfd.fd = SSL_get_rfd(session->ti.tls);
1244 if (pfd.fd < 0) {
1245 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1246 ret = NC_PSPOLL_ERROR;
1247 break;
1248 }
1249 pfd.events = POLLIN;
1250 pfd.revents = 0;
1251 r = poll(&pfd, 1, 0);
1252
1253 if ((r < 0) && (errno != EINTR)) {
1254 sprintf(msg, "poll failed (%s)", strerror(errno));
1255 session->status = NC_STATUS_INVALID;
1256 ret = NC_PSPOLL_ERROR;
1257 } else if (r > 0) {
1258 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1259 sprintf(msg, "communication socket unexpectedly closed");
1260 session->status = NC_STATUS_INVALID;
1261 session->term_reason = NC_SESSION_TERM_DROPPED;
1262 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1263 } else if (pfd.revents & POLLERR) {
1264 sprintf(msg, "communication socket error");
1265 session->status = NC_STATUS_INVALID;
1266 session->term_reason = NC_SESSION_TERM_OTHER;
1267 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1268 } else {
1269 ret = NC_PSPOLL_RPC;
1270 }
1271 } else {
1272 ret = NC_PSPOLL_TIMEOUT;
1273 }
1274 } else {
1275 ret = NC_PSPOLL_RPC;
1276 }
1277 break;
1278#endif
1279 case NC_TI_FD:
1280 pfd.fd = session->ti.fd.in;
1281 pfd.events = POLLIN;
1282 pfd.revents = 0;
1283 r = poll(&pfd, 1, 0);
1284
1285 if ((r < 0) && (errno != EINTR)) {
1286 sprintf(msg, "poll failed (%s)", strerror(errno));
1287 session->status = NC_STATUS_INVALID;
1288 ret = NC_PSPOLL_ERROR;
1289 } else if (r > 0) {
1290 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1291 sprintf(msg, "communication socket unexpectedly closed");
1292 session->status = NC_STATUS_INVALID;
1293 session->term_reason = NC_SESSION_TERM_DROPPED;
1294 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1295 } else if (pfd.revents & POLLERR) {
1296 sprintf(msg, "communication socket error");
1297 session->status = NC_STATUS_INVALID;
1298 session->term_reason = NC_SESSION_TERM_OTHER;
1299 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1300 } else {
1301 ret = NC_PSPOLL_RPC;
1302 }
1303 } else {
1304 ret = NC_PSPOLL_TIMEOUT;
1305 }
1306 break;
1307 case NC_TI_NONE:
1308 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1309 ret = NC_PSPOLL_ERROR;
1310 break;
1311 }
1312
1313 return ret;
1314}
1315
1316API int
1317nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1318{
1319 int ret, r;
1320 uint8_t q_id;
1321 uint16_t i, j;
1322 char msg[256];
1323 struct timespec ts_timeout, ts_cur;
1324 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001325 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001326 struct nc_server_rpc *rpc = NULL;
1327
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001328 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001329 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001330 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001331 }
1332
Michal Vaskoade892d2017-02-22 13:40:35 +01001333 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001334 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001335 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001336 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001337
Michal Vaskoade892d2017-02-22 13:40:35 +01001338 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001339 nc_ps_unlock(ps, q_id, __func__);
1340 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001341 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001342
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001343 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001344 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001345 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001346 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001347 nc_addtimespec(&ts_timeout, timeout);
1348 }
1349
1350 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001351 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001352 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001353 if (ps->last_event_session == ps->session_count - 1) {
1354 i = j = 0;
1355 } else {
1356 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001357 }
Michal Vasko9a327362017-01-11 11:31:46 +01001358 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001359 cur_ps_session = ps->sessions[i];
1360 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001361
Michal Vaskoade892d2017-02-22 13:40:35 +01001362 /* SESSION LOCK */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001363 r = nc_session_lock(cur_session, 0, __func__);
1364 if (r == -1) {
1365 ret = NC_PSPOLL_ERROR;
1366 } else if (r == 1) {
1367 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001368 switch (cur_ps_session->state) {
1369 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001370 if (cur_session->status == NC_STATUS_RUNNING) {
1371 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001372 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001373
1374 ret = nc_ps_poll_session(cur_session, ts_cur.tv_sec, msg);
1375 switch (ret) {
1376 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1377 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001378 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001379 break;
1380 case NC_PSPOLL_ERROR:
1381 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001382 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001383 break;
1384 case NC_PSPOLL_TIMEOUT:
1385#ifdef NC_ENABLED_SSH
1386 case NC_PSPOLL_SSH_CHANNEL:
1387 case NC_PSPOLL_SSH_MSG:
1388#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001389 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001390 break;
1391 case NC_PSPOLL_RPC:
1392 /* let's keep the state busy, we are not done with this session */
1393 break;
1394 }
1395 } else {
1396 /* session is not fine, let the caller know */
1397 ret = NC_PSPOLL_SESSION_TERM;
1398 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1399 ret |= NC_PSPOLL_SESSION_ERROR;
1400 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001401 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001402 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001403 break;
1404 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001405 /* it definitely should not be busy because we have the lock */
1406 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001407 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001408 break;
1409 case NC_PS_STATE_INVALID:
1410 /* we got it locked, but it will be freed, let it be */
1411 ret = NC_PSPOLL_TIMEOUT;
1412 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001413 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001414
1415 /* keep the session locked only in this one case */
1416 if (ret != NC_PSPOLL_RPC) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001417 /* SESSION UNLOCK */
1418 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001419 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001420 } else {
1421 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001422 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001423 }
Michal Vasko428087d2016-01-14 16:04:28 +01001424
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001425 /* something happened */
1426 if (ret != NC_PSPOLL_TIMEOUT) {
1427 break;
1428 }
1429
Michal Vasko9a327362017-01-11 11:31:46 +01001430 if (i == ps->session_count - 1) {
1431 i = 0;
1432 } else {
1433 ++i;
1434 }
1435 } while (i != j);
1436
Michal Vaskoade892d2017-02-22 13:40:35 +01001437 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001438 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001439 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001440 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001441 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001442
1443 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1444 /* final timeout */
1445 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001446 }
Michal Vasko428087d2016-01-14 16:04:28 +01001447 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001448 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001449
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001450 /* do we want to return the session? */
1451 switch (ret) {
1452 case NC_PSPOLL_RPC:
1453 case NC_PSPOLL_SESSION_TERM:
1454 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1455#ifdef NC_ENABLED_SSH
1456 case NC_PSPOLL_SSH_CHANNEL:
1457 case NC_PSPOLL_SSH_MSG:
1458#endif
1459 if (session) {
1460 *session = cur_session;
1461 }
1462 ps->last_event_session = i;
1463 break;
1464 default:
1465 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001466 }
Michal Vasko428087d2016-01-14 16:04:28 +01001467
Michal Vaskoade892d2017-02-22 13:40:35 +01001468 /* PS UNLOCK */
1469 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001470
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001471 /* we have some data available and the session is locked */
1472 if (ret == NC_PSPOLL_RPC) {
1473 ret = nc_server_recv_rpc(cur_session, &rpc);
1474 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1475 if (cur_session->status != NC_STATUS_RUNNING) {
1476 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001477 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001478 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001479 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001480 }
1481 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001482 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001483
1484 /* process RPC, not needed afterwards */
1485 ret |= nc_server_send_reply(cur_session, rpc);
1486 nc_server_rpc_free(rpc, server_opts.ctx);
1487
1488 if (cur_session->status != NC_STATUS_RUNNING) {
1489 ret |= NC_PSPOLL_SESSION_TERM;
1490 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1491 ret |= NC_PSPOLL_SESSION_ERROR;
1492 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001493 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001494 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001495 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001496 }
Michal Vasko428087d2016-01-14 16:04:28 +01001497 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001498
1499 /* SESSION UNLOCK */
1500 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001501 }
1502
Michal Vasko48a63ed2016-03-01 09:48:21 +01001503 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001504}
1505
Michal Vaskod09eae62016-02-01 10:32:52 +01001506API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001507nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001508{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001509 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001510 uint16_t i;
1511 struct nc_session *session;
1512
Michal Vasko9a25e932016-02-01 10:36:42 +01001513 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001514 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001515 return;
1516 }
1517
Michal Vasko48a63ed2016-03-01 09:48:21 +01001518 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001519 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001520 return;
1521 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001522
Michal Vasko48a63ed2016-03-01 09:48:21 +01001523 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001524 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001525 nc_session_free(ps->sessions[i]->session, data_free);
1526 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001527 }
1528 free(ps->sessions);
1529 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001530 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001531 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001532 } else {
1533 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001534 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1535 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001536 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001537 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001538 continue;
1539 }
1540
1541 ++i;
1542 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001543 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001544
1545 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001546 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001547}
1548
Radek Krejci53691be2016-02-22 13:58:37 +01001549#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001550
Michal Vaskoe2713da2016-08-22 16:06:40 +02001551API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001552nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001553{
Michal Vasko3031aae2016-01-27 16:07:18 +01001554 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001555 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001556
Michal Vasko45e53ae2016-04-07 11:46:03 +02001557 if (!name) {
1558 ERRARG("name");
1559 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001560 }
1561
Michal Vaskoade892d2017-02-22 13:40:35 +01001562 /* BIND LOCK */
1563 pthread_mutex_lock(&server_opts.bind_lock);
1564
1565 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001566 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001567
1568 /* check name uniqueness */
1569 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001570 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001571 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001572 ret = -1;
1573 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001574 }
1575 }
1576
Michal Vasko3031aae2016-01-27 16:07:18 +01001577 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001578 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001579 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001580 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001581 ret = -1;
1582 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001583 }
1584 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001585 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001586
Michal Vaskoe2713da2016-08-22 16:06:40 +02001587 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001588 if (!server_opts.binds) {
1589 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001590 ret = -1;
1591 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001592 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001593
Michal Vasko2e6defd2016-10-07 15:48:15 +02001594 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1595 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1596 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001597 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001598
1599 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001600#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001601 case NC_TI_LIBSSH:
1602 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1603 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1604 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001605 ret = -1;
1606 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001607 }
1608 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1609 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1610 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1611 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1612 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001613#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001614#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001615 case NC_TI_OPENSSL:
1616 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1617 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1618 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001619 ret = -1;
1620 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001621 }
1622 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001623#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001624 default:
1625 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001626 ret = -1;
1627 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001628 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001629
Michal Vaskoade892d2017-02-22 13:40:35 +01001630cleanup:
1631 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001632 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001633
Michal Vaskoade892d2017-02-22 13:40:35 +01001634 /* BIND UNLOCK */
1635 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001636
Michal Vaskoade892d2017-02-22 13:40:35 +01001637 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001638}
1639
Michal Vasko3031aae2016-01-27 16:07:18 +01001640int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001641nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001642{
1643 struct nc_endpt *endpt;
1644 struct nc_bind *bind = NULL;
1645 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001646 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001647
Michal Vasko45e53ae2016-04-07 11:46:03 +02001648 if (!endpt_name) {
1649 ERRARG("endpt_name");
1650 return -1;
1651 } else if ((!address && !port) || (address && port)) {
1652 ERRARG("address and port");
1653 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001654 }
1655
Michal Vaskoe2713da2016-08-22 16:06:40 +02001656 if (address) {
1657 set_addr = 1;
1658 } else {
1659 set_addr = 0;
1660 }
1661
Michal Vaskoade892d2017-02-22 13:40:35 +01001662 /* BIND LOCK */
1663 pthread_mutex_lock(&server_opts.bind_lock);
1664
1665 /* ENDPT LOCK */
1666 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001667 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001668 /* BIND UNLOCK */
1669 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001670 return -1;
1671 }
1672
Michal Vaskoe2713da2016-08-22 16:06:40 +02001673 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001674
Michal Vaskoe2713da2016-08-22 16:06:40 +02001675 if (set_addr) {
1676 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001677 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001678 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001679 }
1680
Michal Vaskoe2713da2016-08-22 16:06:40 +02001681 /* we have all the information we need to create a listening socket */
1682 if (address && port) {
1683 /* create new socket, close the old one */
1684 sock = nc_sock_listen(address, port);
1685 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001686 ret = -1;
1687 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001688 }
1689
1690 if (bind->sock > -1) {
1691 close(bind->sock);
1692 }
1693 bind->sock = sock;
1694 } /* else we are just setting address or port */
1695
1696 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001697 lydict_remove(server_opts.ctx, bind->address);
1698 bind->address = lydict_insert(server_opts.ctx, address, 0);
1699 } else {
1700 bind->port = port;
1701 }
1702
Michal Vaskoe2713da2016-08-22 16:06:40 +02001703 if (sock > -1) {
1704#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001705 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001706#elif defined(NC_ENABLED_SSH)
1707 VRB("Listening on %s:%u for SSH connections.", address, port);
1708#else
1709 VRB("Listening on %s:%u for TLS connections.", address, port);
1710#endif
1711 }
1712
Michal Vasko4c1fb492017-01-30 14:31:07 +01001713cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001714 /* ENDPT UNLOCK */
1715 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001716
Michal Vaskoade892d2017-02-22 13:40:35 +01001717 /* BIND UNLOCK */
1718 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001719
Michal Vasko4c1fb492017-01-30 14:31:07 +01001720 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001721}
1722
Michal Vaskoe2713da2016-08-22 16:06:40 +02001723API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001724nc_server_endpt_set_address(const char *endpt_name, const char *address)
1725{
1726 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1727}
1728
1729API int
1730nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1731{
1732 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1733}
1734
1735API int
Michal Vasko59050372016-11-22 14:33:55 +01001736nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001737{
1738 uint32_t i;
1739 int ret = -1;
1740
Michal Vaskoade892d2017-02-22 13:40:35 +01001741 /* BIND LOCK */
1742 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001743
Michal Vaskoade892d2017-02-22 13:40:35 +01001744 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001745 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001746
Michal Vasko59050372016-11-22 14:33:55 +01001747 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001748 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001749 for (i = 0; i < server_opts.endpt_count; ++i) {
1750 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001751 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001752#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001753 case NC_TI_LIBSSH:
1754 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1755 free(server_opts.endpts[i].opts.ssh);
1756 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001757#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001758#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001759 case NC_TI_OPENSSL:
1760 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1761 free(server_opts.endpts[i].opts.tls);
1762 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001763#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001764 default:
1765 ERRINT;
1766 /* won't get here ...*/
1767 break;
1768 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001769 ret = 0;
1770 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001771 free(server_opts.endpts);
1772 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001773
1774 /* remove all binds */
1775 for (i = 0; i < server_opts.endpt_count; ++i) {
1776 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1777 if (server_opts.binds[i].sock > -1) {
1778 close(server_opts.binds[i].sock);
1779 }
1780 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001781 free(server_opts.binds);
1782 server_opts.binds = NULL;
1783
Michal Vasko3031aae2016-01-27 16:07:18 +01001784 server_opts.endpt_count = 0;
1785
Michal Vasko1a38c862016-01-15 15:50:07 +01001786 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001787 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001788 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001789 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001790 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001791 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001792 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001793#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001794 case NC_TI_LIBSSH:
1795 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1796 free(server_opts.endpts[i].opts.ssh);
1797 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001798#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001799#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001800 case NC_TI_OPENSSL:
1801 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1802 free(server_opts.endpts[i].opts.tls);
1803 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001804#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001805 default:
1806 ERRINT;
1807 break;
1808 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001809
Michal Vaskoe2713da2016-08-22 16:06:40 +02001810 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001811 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1812 if (server_opts.binds[i].sock > -1) {
1813 close(server_opts.binds[i].sock);
1814 }
1815
1816 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001817 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001818 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001819 free(server_opts.binds);
1820 server_opts.binds = NULL;
1821 free(server_opts.endpts);
1822 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001823 } else if (i < server_opts.endpt_count) {
1824 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1825 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001826 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001827
1828 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001829 if (name) {
1830 break;
1831 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001832 }
1833 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001834 }
1835
Michal Vaskoade892d2017-02-22 13:40:35 +01001836 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001837 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001838
Michal Vaskoade892d2017-02-22 13:40:35 +01001839 /* BIND UNLOCK */
1840 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001841
1842 return ret;
1843}
1844
Michal Vasko71090fc2016-05-24 16:37:28 +02001845API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001846nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001847{
Michal Vasko71090fc2016-05-24 16:37:28 +02001848 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001849 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001850 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001851 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02001852 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01001853
Michal Vasko45e53ae2016-04-07 11:46:03 +02001854 if (!server_opts.ctx) {
1855 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001856 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001857 } else if (!session) {
1858 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001859 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001860 }
1861
Michal Vaskoade892d2017-02-22 13:40:35 +01001862 /* BIND LOCK */
1863 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001864
1865 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001866 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01001867 /* BIND UNLOCK */
1868 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001869 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001870 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001871
Michal Vaskoe2713da2016-08-22 16:06:40 +02001872 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001873 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001874 /* BIND UNLOCK */
1875 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001876 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001877 if (!ret) {
1878 return NC_MSG_WOULDBLOCK;
1879 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001880 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001881 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001882
1883 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
1884 /* ENDPT READ LOCK */
1885 pthread_rwlock_rdlock(&server_opts.endpt_lock);
1886
1887 /* BIND UNLOCK */
1888 pthread_mutex_unlock(&server_opts.bind_lock);
1889
Michal Vaskob48aa812016-01-18 14:13:09 +01001890 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001891
Michal Vaskoade892d2017-02-22 13:40:35 +01001892 *session = nc_new_session(0);
Michal Vasko686aa312016-01-21 15:58:18 +01001893 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001894 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001895 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001896 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001897 msgtype = NC_MSG_ERROR;
1898 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001899 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001900 (*session)->status = NC_STATUS_STARTING;
1901 (*session)->side = NC_SERVER;
1902 (*session)->ctx = server_opts.ctx;
1903 (*session)->flags = NC_SESSION_SHAREDCTX;
1904 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1905 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001906
1907 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001908 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001909 pthread_cond_init((*session)->ti_cond, NULL);
1910 *(*session)->ti_inuse = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001911
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001912 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001913#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001914 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1915 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001916 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001917 if (ret < 0) {
1918 msgtype = NC_MSG_ERROR;
1919 goto cleanup;
1920 } else if (!ret) {
1921 msgtype = NC_MSG_WOULDBLOCK;
1922 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001923 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001924 } else
1925#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001926#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001927 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1928 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001929 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001930 if (ret < 0) {
1931 msgtype = NC_MSG_ERROR;
1932 goto cleanup;
1933 } else if (!ret) {
1934 msgtype = NC_MSG_WOULDBLOCK;
1935 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001936 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001937 } else
1938#endif
1939 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001940 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001941 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001942 msgtype = NC_MSG_ERROR;
1943 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001944 }
1945
Michal Vasko2cc4c682016-03-01 09:16:48 +01001946 (*session)->data = NULL;
1947
Michal Vaskoade892d2017-02-22 13:40:35 +01001948 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001949 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001950
Michal Vaskob48aa812016-01-18 14:13:09 +01001951 /* assign new SID atomically */
1952 /* LOCK */
1953 pthread_spin_lock(&server_opts.sid_lock);
1954 (*session)->id = server_opts.new_session_id++;
1955 /* UNLOCK */
1956 pthread_spin_unlock(&server_opts.sid_lock);
1957
Michal Vasko9e036d52016-01-08 10:49:26 +01001958 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001959 msgtype = nc_handshake(*session);
1960 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001961 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001962 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001963 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001964 }
Michal Vasko9fb42272017-10-05 13:50:05 +02001965
1966 nc_gettimespec_mono(&ts_cur);
1967 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
1968 nc_gettimespec_real(&ts_cur);
1969 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01001970 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001971
Michal Vasko71090fc2016-05-24 16:37:28 +02001972 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001973
Michal Vasko71090fc2016-05-24 16:37:28 +02001974cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001975 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001976 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001977
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001978 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001979 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001980 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001981}
1982
Michal Vasko2e6defd2016-10-07 15:48:15 +02001983API int
1984nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1985{
1986 uint16_t i;
1987
1988 if (!name) {
1989 ERRARG("name");
1990 return -1;
1991 } else if (!ti) {
1992 ERRARG("ti");
1993 return -1;
1994 }
1995
1996 /* WRITE LOCK */
1997 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1998
1999 /* check name uniqueness */
2000 for (i = 0; i < server_opts.ch_client_count; ++i) {
2001 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2002 ERR("Call Home client \"%s\" already exists.", name);
2003 /* WRITE UNLOCK */
2004 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2005 return -1;
2006 }
2007 }
2008
2009 ++server_opts.ch_client_count;
2010 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2011 if (!server_opts.ch_clients) {
2012 ERRMEM;
2013 /* WRITE UNLOCK */
2014 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2015 return -1;
2016 }
2017 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
2018 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002019 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
2020 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002021
2022 switch (ti) {
2023#ifdef NC_ENABLED_SSH
2024 case NC_TI_LIBSSH:
2025 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2026 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
2027 ERRMEM;
2028 /* WRITE UNLOCK */
2029 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2030 return -1;
2031 }
2032 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
2033 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2034 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
2035 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
2036 break;
2037#endif
2038#ifdef NC_ENABLED_TLS
2039 case NC_TI_OPENSSL:
2040 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2041 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
2042 ERRMEM;
2043 /* WRITE UNLOCK */
2044 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2045 return -1;
2046 }
2047 break;
2048#endif
2049 default:
2050 ERRINT;
2051 /* WRITE UNLOCK */
2052 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2053 return -1;
2054 }
2055
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002056 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
2057
Michal Vasko2e6defd2016-10-07 15:48:15 +02002058 /* set CH default options */
2059 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
2060 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
2061
2062 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
2063
2064 /* WRITE UNLOCK */
2065 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2066
2067 return 0;
2068}
2069
2070API int
Michal Vasko59050372016-11-22 14:33:55 +01002071nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002072{
2073 uint16_t i, j;
2074 int ret = -1;
2075
2076 /* WRITE LOCK */
2077 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2078
Michal Vasko59050372016-11-22 14:33:55 +01002079 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002080 /* remove all CH clients */
2081 for (i = 0; i < server_opts.ch_client_count; ++i) {
2082 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2083
2084 /* remove all endpoints */
2085 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2086 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2087 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2088 }
2089 free(server_opts.ch_clients[i].ch_endpts);
2090
2091 switch (server_opts.ch_clients[i].ti) {
2092#ifdef NC_ENABLED_SSH
2093 case NC_TI_LIBSSH:
2094 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2095 free(server_opts.ch_clients[i].opts.ssh);
2096 break;
2097#endif
2098#ifdef NC_ENABLED_TLS
2099 case NC_TI_OPENSSL:
2100 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2101 free(server_opts.ch_clients[i].opts.tls);
2102 break;
2103#endif
2104 default:
2105 ERRINT;
2106 /* won't get here ...*/
2107 break;
2108 }
2109
2110 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2111
2112 ret = 0;
2113 }
2114 free(server_opts.ch_clients);
2115 server_opts.ch_clients = NULL;
2116
2117 server_opts.ch_client_count = 0;
2118
2119 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002120 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002121 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002122 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 +02002123 /* remove endpt */
2124 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2125
2126 switch (server_opts.ch_clients[i].ti) {
2127#ifdef NC_ENABLED_SSH
2128 case NC_TI_LIBSSH:
2129 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2130 free(server_opts.ch_clients[i].opts.ssh);
2131 break;
2132#endif
2133#ifdef NC_ENABLED_TLS
2134 case NC_TI_OPENSSL:
2135 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2136 free(server_opts.ch_clients[i].opts.tls);
2137 break;
2138#endif
2139 default:
2140 ERRINT;
2141 break;
2142 }
2143
2144 /* remove all endpoints */
2145 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2146 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2147 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2148 }
2149 free(server_opts.ch_clients[i].ch_endpts);
2150
2151 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2152
2153 /* move last client and endpoint(s) to the empty space */
2154 --server_opts.ch_client_count;
2155 if (i < server_opts.ch_client_count) {
2156 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2157 sizeof *server_opts.ch_clients);
2158 } else if (!server_opts.ch_client_count) {
2159 free(server_opts.ch_clients);
2160 server_opts.ch_clients = NULL;
2161 }
2162
2163 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002164 if (name) {
2165 break;
2166 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002167 }
2168 }
2169 }
2170
2171 /* WRITE UNLOCK */
2172 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2173
2174 return ret;
2175}
2176
2177API int
2178nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2179{
2180 uint16_t i;
2181 struct nc_ch_client *client;
2182
2183 if (!client_name) {
2184 ERRARG("client_name");
2185 return -1;
2186 } else if (!endpt_name) {
2187 ERRARG("endpt_name");
2188 return -1;
2189 }
2190
2191 /* LOCK */
2192 client = nc_server_ch_client_lock(client_name, 0, NULL);
2193 if (!client) {
2194 return -1;
2195 }
2196
2197 for (i = 0; i < client->ch_endpt_count; ++i) {
2198 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2199 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2200 /* UNLOCK */
2201 nc_server_ch_client_unlock(client);
2202 return -1;
2203 }
2204 }
2205
2206 ++client->ch_endpt_count;
2207 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2208 if (!client->ch_endpts) {
2209 ERRMEM;
2210 /* UNLOCK */
2211 nc_server_ch_client_unlock(client);
2212 return -1;
2213 }
2214
2215 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2216 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2217 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
2218
2219 /* UNLOCK */
2220 nc_server_ch_client_unlock(client);
2221
2222 return 0;
2223}
2224
2225API int
2226nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2227{
2228 uint16_t i;
2229 int ret = -1;
2230 struct nc_ch_client *client;
2231
2232 if (!client_name) {
2233 ERRARG("client_name");
2234 return -1;
2235 }
2236
2237 /* LOCK */
2238 client = nc_server_ch_client_lock(client_name, 0, NULL);
2239 if (!client) {
2240 return -1;
2241 }
2242
2243 if (!endpt_name) {
2244 /* remove all endpoints */
2245 for (i = 0; i < client->ch_endpt_count; ++i) {
2246 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2247 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2248 }
2249 free(client->ch_endpts);
2250 client->ch_endpts = NULL;
2251 client->ch_endpt_count = 0;
2252
2253 ret = 0;
2254 } else {
2255 for (i = 0; i < client->ch_endpt_count; ++i) {
2256 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2257 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2258 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002259
Michal Vasko4f921012016-10-20 14:07:45 +02002260 /* move last endpoint to the empty space */
2261 --client->ch_endpt_count;
2262 if (i < client->ch_endpt_count) {
2263 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2264 } else if (!server_opts.ch_client_count) {
2265 free(server_opts.ch_clients);
2266 server_opts.ch_clients = NULL;
2267 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002268
Michal Vasko4f921012016-10-20 14:07:45 +02002269 ret = 0;
2270 break;
2271 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002272 }
2273 }
2274
2275 /* UNLOCK */
2276 nc_server_ch_client_unlock(client);
2277
2278 return ret;
2279}
2280
2281API int
2282nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2283{
2284 uint16_t i;
2285 int ret = -1;
2286 struct nc_ch_client *client;
2287
2288 if (!client_name) {
2289 ERRARG("client_name");
2290 return -1;
2291 } else if (!endpt_name) {
2292 ERRARG("endpt_name");
2293 return -1;
2294 } else if (!address) {
2295 ERRARG("address");
2296 return -1;
2297 }
2298
2299 /* LOCK */
2300 client = nc_server_ch_client_lock(client_name, 0, NULL);
2301 if (!client) {
2302 return -1;
2303 }
2304
2305 for (i = 0; i < client->ch_endpt_count; ++i) {
2306 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2307 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2308 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2309
2310 ret = 0;
2311 break;
2312 }
2313 }
2314
2315 /* UNLOCK */
2316 nc_server_ch_client_unlock(client);
2317
2318 if (ret == -1) {
2319 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2320 }
2321
2322 return ret;
2323}
2324
2325API int
2326nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2327{
2328 uint16_t i;
2329 int ret = -1;
2330 struct nc_ch_client *client;
2331
2332 if (!client_name) {
2333 ERRARG("client_name");
2334 return -1;
2335 } else if (!endpt_name) {
2336 ERRARG("endpt_name");
2337 return -1;
2338 } else if (!port) {
2339 ERRARG("port");
2340 return -1;
2341 }
2342
2343 /* LOCK */
2344 client = nc_server_ch_client_lock(client_name, 0, NULL);
2345 if (!client) {
2346 return -1;
2347 }
2348
2349 for (i = 0; i < client->ch_endpt_count; ++i) {
2350 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2351 client->ch_endpts[i].port = port;
2352
2353 ret = 0;
2354 break;
2355 }
2356 }
2357
2358 /* UNLOCK */
2359 nc_server_ch_client_unlock(client);
2360
2361 if (ret == -1) {
2362 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2363 }
2364
2365 return ret;
2366}
2367
2368API int
2369nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2370{
2371 struct nc_ch_client *client;
2372
2373 if (!client_name) {
2374 ERRARG("client_name");
2375 return -1;
2376 } else if (!conn_type) {
2377 ERRARG("conn_type");
2378 return -1;
2379 }
2380
2381 /* LOCK */
2382 client = nc_server_ch_client_lock(client_name, 0, NULL);
2383 if (!client) {
2384 return -1;
2385 }
2386
2387 if (client->conn_type != conn_type) {
2388 client->conn_type = conn_type;
2389
2390 /* set default options */
2391 switch (conn_type) {
2392 case NC_CH_PERSIST:
2393 client->conn.persist.idle_timeout = 86400;
2394 client->conn.persist.ka_max_wait = 30;
2395 client->conn.persist.ka_max_attempts = 3;
2396 break;
2397 case NC_CH_PERIOD:
2398 client->conn.period.idle_timeout = 300;
2399 client->conn.period.reconnect_timeout = 60;
2400 break;
2401 default:
2402 ERRINT;
2403 break;
2404 }
2405 }
2406
2407 /* UNLOCK */
2408 nc_server_ch_client_unlock(client);
2409
2410 return 0;
2411}
2412
2413API int
2414nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2415{
2416 struct nc_ch_client *client;
2417
2418 if (!client_name) {
2419 ERRARG("client_name");
2420 return -1;
2421 }
2422
2423 /* LOCK */
2424 client = nc_server_ch_client_lock(client_name, 0, NULL);
2425 if (!client) {
2426 return -1;
2427 }
2428
2429 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002430 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002431 /* UNLOCK */
2432 nc_server_ch_client_unlock(client);
2433 return -1;
2434 }
2435
2436 client->conn.persist.idle_timeout = idle_timeout;
2437
2438 /* UNLOCK */
2439 nc_server_ch_client_unlock(client);
2440
2441 return 0;
2442}
2443
2444API int
2445nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2446{
2447 struct nc_ch_client *client;
2448
2449 if (!client_name) {
2450 ERRARG("client_name");
2451 return -1;
2452 } else if (!max_wait) {
2453 ERRARG("max_wait");
2454 return -1;
2455 }
2456
2457 /* LOCK */
2458 client = nc_server_ch_client_lock(client_name, 0, NULL);
2459 if (!client) {
2460 return -1;
2461 }
2462
2463 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002464 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002465 /* UNLOCK */
2466 nc_server_ch_client_unlock(client);
2467 return -1;
2468 }
2469
2470 client->conn.persist.ka_max_wait = max_wait;
2471
2472 /* UNLOCK */
2473 nc_server_ch_client_unlock(client);
2474
2475 return 0;
2476}
2477
2478API int
2479nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2480{
2481 struct nc_ch_client *client;
2482
2483 if (!client_name) {
2484 ERRARG("client_name");
2485 return -1;
2486 }
2487
2488 /* LOCK */
2489 client = nc_server_ch_client_lock(client_name, 0, NULL);
2490 if (!client) {
2491 return -1;
2492 }
2493
2494 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002495 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002496 /* UNLOCK */
2497 nc_server_ch_client_unlock(client);
2498 return -1;
2499 }
2500
2501 client->conn.persist.ka_max_attempts = max_attempts;
2502
2503 /* UNLOCK */
2504 nc_server_ch_client_unlock(client);
2505
2506 return 0;
2507}
2508
2509API int
2510nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2511{
2512 struct nc_ch_client *client;
2513
2514 if (!client_name) {
2515 ERRARG("client_name");
2516 return -1;
2517 }
2518
2519 /* LOCK */
2520 client = nc_server_ch_client_lock(client_name, 0, NULL);
2521 if (!client) {
2522 return -1;
2523 }
2524
2525 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002526 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002527 /* UNLOCK */
2528 nc_server_ch_client_unlock(client);
2529 return -1;
2530 }
2531
2532 client->conn.period.idle_timeout = idle_timeout;
2533
2534 /* UNLOCK */
2535 nc_server_ch_client_unlock(client);
2536
2537 return 0;
2538}
2539
2540API int
2541nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2542{
2543 struct nc_ch_client *client;
2544
2545 if (!client_name) {
2546 ERRARG("client_name");
2547 return -1;
2548 } else if (!reconnect_timeout) {
2549 ERRARG("reconnect_timeout");
2550 return -1;
2551 }
2552
2553 /* LOCK */
2554 client = nc_server_ch_client_lock(client_name, 0, NULL);
2555 if (!client) {
2556 return -1;
2557 }
2558
2559 if (client->conn_type != NC_CH_PERIOD) {
2560 ERR("Call Home client \"%s\" is not of periodic connection type.");
2561 /* UNLOCK */
2562 nc_server_ch_client_unlock(client);
2563 return -1;
2564 }
2565
2566 client->conn.period.reconnect_timeout = reconnect_timeout;
2567
2568 /* UNLOCK */
2569 nc_server_ch_client_unlock(client);
2570
2571 return 0;
2572}
2573
2574API int
2575nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2576{
2577 struct nc_ch_client *client;
2578
2579 if (!client_name) {
2580 ERRARG("client_name");
2581 return -1;
2582 }
2583
2584 /* LOCK */
2585 client = nc_server_ch_client_lock(client_name, 0, NULL);
2586 if (!client) {
2587 return -1;
2588 }
2589
2590 client->start_with = start_with;
2591
2592 /* UNLOCK */
2593 nc_server_ch_client_unlock(client);
2594
2595 return 0;
2596}
2597
2598API int
2599nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2600{
2601 struct nc_ch_client *client;
2602
2603 if (!client_name) {
2604 ERRARG("client_name");
2605 return -1;
2606 } else if (!max_attempts) {
2607 ERRARG("max_attempts");
2608 return -1;
2609 }
2610
2611 /* LOCK */
2612 client = nc_server_ch_client_lock(client_name, 0, NULL);
2613 if (!client) {
2614 return -1;
2615 }
2616
2617 client->max_attempts = max_attempts;
2618
2619 /* UNLOCK */
2620 nc_server_ch_client_unlock(client);
2621
2622 return 0;
2623}
2624
2625/* client lock is expected to be held */
2626static NC_MSG_TYPE
2627nc_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 +01002628{
Michal Vasko71090fc2016-05-24 16:37:28 +02002629 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002630 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002631 struct timespec ts_cur;
Michal Vaskob05053d2016-01-22 16:12:06 +01002632
Michal Vasko2e6defd2016-10-07 15:48:15 +02002633 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002634 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002635 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002636 }
2637
Michal Vaskoade892d2017-02-22 13:40:35 +01002638 *session = nc_new_session(0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002639 if (!(*session)) {
2640 ERRMEM;
2641 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002642 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002643 }
2644 (*session)->status = NC_STATUS_STARTING;
2645 (*session)->side = NC_SERVER;
2646 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002647 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002648 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2649 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002650
2651 /* transport lock */
Michal Vaskob05053d2016-01-22 16:12:06 +01002652 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01002653 pthread_cond_init((*session)->ti_cond, NULL);
2654 *(*session)->ti_inuse = 0;
Michal Vaskob05053d2016-01-22 16:12:06 +01002655
2656 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002657#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002658 if (client->ti == NC_TI_LIBSSH) {
2659 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002660 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002661 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002662
Michal Vasko71090fc2016-05-24 16:37:28 +02002663 if (ret < 0) {
2664 msgtype = NC_MSG_ERROR;
2665 goto fail;
2666 } else if (!ret) {
2667 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002668 goto fail;
2669 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002670 } else
2671#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002672#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002673 if (client->ti == NC_TI_OPENSSL) {
2674 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002675 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002676 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002677
Michal Vasko71090fc2016-05-24 16:37:28 +02002678 if (ret < 0) {
2679 msgtype = NC_MSG_ERROR;
2680 goto fail;
2681 } else if (!ret) {
2682 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002683 goto fail;
2684 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002685 } else
2686#endif
2687 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002688 ERRINT;
2689 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002690 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002691 goto fail;
2692 }
2693
2694 /* assign new SID atomically */
2695 /* LOCK */
2696 pthread_spin_lock(&server_opts.sid_lock);
2697 (*session)->id = server_opts.new_session_id++;
2698 /* UNLOCK */
2699 pthread_spin_unlock(&server_opts.sid_lock);
2700
2701 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002702 msgtype = nc_handshake(*session);
2703 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002704 goto fail;
2705 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002706
2707 nc_gettimespec_mono(&ts_cur);
2708 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2709 nc_gettimespec_real(&ts_cur);
2710 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01002711 (*session)->status = NC_STATUS_RUNNING;
2712
Michal Vasko71090fc2016-05-24 16:37:28 +02002713 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002714
2715fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002716 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002717 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002718 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002719}
2720
Michal Vasko2e6defd2016-10-07 15:48:15 +02002721struct nc_ch_client_thread_arg {
2722 char *client_name;
2723 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2724};
2725
2726static struct nc_ch_client *
2727nc_server_ch_client_with_endpt_lock(const char *name)
2728{
2729 struct nc_ch_client *client;
2730
2731 while (1) {
2732 /* LOCK */
2733 client = nc_server_ch_client_lock(name, 0, NULL);
2734 if (!client) {
2735 return NULL;
2736 }
2737 if (client->ch_endpt_count) {
2738 return client;
2739 }
2740 /* no endpoints defined yet */
2741
2742 /* UNLOCK */
2743 nc_server_ch_client_unlock(client);
2744
2745 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2746 }
2747
2748 return NULL;
2749}
2750
2751static int
2752nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2753{
2754 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002755 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002756 struct timespec ts;
2757 struct nc_ch_client *client;
2758
2759 /* session created, initialize condition */
2760 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2761 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2762 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2763 ERRMEM;
2764 nc_session_free(session, NULL);
2765 return -1;
2766 }
2767 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2768 pthread_cond_init(session->opts.server.ch_cond, NULL);
2769
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002770 session->flags |= NC_SESSION_CALLHOME;
2771
Michal Vasko2e6defd2016-10-07 15:48:15 +02002772 /* CH LOCK */
2773 pthread_mutex_lock(session->opts.server.ch_lock);
2774
2775 /* give the session to the user */
2776 data->session_clb(data->client_name, session);
2777
2778 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02002779 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01002780 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002781
2782 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2783 if (ret && (ret != ETIMEDOUT)) {
2784 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2785 goto ch_client_remove;
2786 }
2787
2788 /* check whether the client was not removed */
2789 /* LOCK */
2790 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2791 if (!client) {
2792 /* client was removed, finish thread */
2793 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2794 data->client_name);
2795 goto ch_client_remove;
2796 }
2797
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002798 if (client->conn_type == NC_CH_PERSIST) {
2799 /* TODO keep-alives */
2800 idle_timeout = client->conn.persist.idle_timeout;
2801 } else {
2802 idle_timeout = client->conn.period.idle_timeout;
2803 }
2804
Michal Vasko9fb42272017-10-05 13:50:05 +02002805 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01002806 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 +02002807 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2808 session->status = NC_STATUS_INVALID;
2809 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2810 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002811
2812 /* UNLOCK */
2813 nc_server_ch_client_unlock(client);
2814
2815 } while (session->status == NC_STATUS_RUNNING);
2816
2817 /* CH UNLOCK */
2818 pthread_mutex_unlock(session->opts.server.ch_lock);
2819
2820 return 0;
2821
2822ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002823 /* make the session a standard one */
2824 pthread_cond_destroy(session->opts.server.ch_cond);
2825 free(session->opts.server.ch_cond);
2826 session->opts.server.ch_cond = NULL;
2827
2828 session->flags &= ~NC_SESSION_CALLHOME;
2829
Michal Vasko2e6defd2016-10-07 15:48:15 +02002830 /* CH UNLOCK */
2831 pthread_mutex_unlock(session->opts.server.ch_lock);
2832
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002833 pthread_mutex_destroy(session->opts.server.ch_lock);
2834 free(session->opts.server.ch_lock);
2835 session->opts.server.ch_lock = NULL;
2836
Michal Vasko2e6defd2016-10-07 15:48:15 +02002837 return 1;
2838}
2839
2840static void *
2841nc_ch_client_thread(void *arg)
2842{
2843 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2844 NC_MSG_TYPE msgtype;
2845 uint8_t cur_attempts = 0;
2846 uint16_t i;
Michal Vasko9550cf12017-03-21 15:33:58 +01002847 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002848 struct nc_ch_endpt *cur_endpt;
2849 struct nc_session *session;
2850 struct nc_ch_client *client;
2851
2852 /* LOCK */
2853 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2854 if (!client) {
2855 goto cleanup;
2856 }
2857
2858 cur_endpt = &client->ch_endpts[0];
2859 cur_endpt_name = strdup(cur_endpt->name);
2860
Michal Vasko29af44b2016-10-13 10:59:55 +02002861 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002862 while (1) {
2863 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2864
2865 if (msgtype == NC_MSG_HELLO) {
2866 /* UNLOCK */
2867 nc_server_ch_client_unlock(client);
2868
Michal Vasko29af44b2016-10-13 10:59:55 +02002869 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002870 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2871 goto cleanup;
2872 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002873 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002874
2875 /* LOCK */
2876 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2877 if (!client) {
2878 goto cleanup;
2879 }
2880
2881 /* session changed status -> it was disconnected for whatever reason,
2882 * persistent connection immediately tries to reconnect, periodic waits some first */
2883 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002884 /* UNLOCK */
2885 nc_server_ch_client_unlock(client);
2886
2887 /* TODO wake up sometimes to check for new notifications */
2888 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2889
2890 /* LOCK */
2891 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2892 if (!client) {
2893 goto cleanup;
2894 }
2895 }
2896
2897 /* set next endpoint to try */
2898 if (client->start_with == NC_CH_FIRST_LISTED) {
2899 cur_endpt = &client->ch_endpts[0];
2900 free(cur_endpt_name);
2901 cur_endpt_name = strdup(cur_endpt->name);
2902 } /* else we keep the current one */
2903 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02002904 /* UNLOCK */
2905 nc_server_ch_client_unlock(client);
2906
Michal Vasko2e6defd2016-10-07 15:48:15 +02002907 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002908 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2909
Michal Vasko6bb116b2016-10-26 13:53:46 +02002910 /* LOCK */
2911 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2912 if (!client) {
2913 goto cleanup;
2914 }
2915
Michal Vasko2e6defd2016-10-07 15:48:15 +02002916 ++cur_attempts;
2917 if (cur_attempts == client->max_attempts) {
2918 for (i = 0; i < client->ch_endpt_count; ++i) {
2919 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2920 break;
2921 }
2922 }
2923 if (i < client->ch_endpt_count - 1) {
2924 /* just go to the next endpoint */
2925 cur_endpt = &client->ch_endpts[i + 1];
2926 free(cur_endpt_name);
2927 cur_endpt_name = strdup(cur_endpt->name);
2928 } else {
2929 /* cur_endpoint was removed or is the last, either way start with the first one */
2930 cur_endpt = &client->ch_endpts[0];
2931 free(cur_endpt_name);
2932 cur_endpt_name = strdup(cur_endpt->name);
2933 }
2934
2935 cur_attempts = 0;
2936 } /* else we keep the current one */
2937 }
2938 }
2939
2940cleanup:
2941 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01002942 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002943 free(data->client_name);
2944 free(data);
2945 return NULL;
2946}
2947
2948API int
2949nc_connect_ch_client_dispatch(const char *client_name,
2950 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2951 int ret;
2952 pthread_t tid;
2953 struct nc_ch_client_thread_arg *arg;
2954
2955 if (!client_name) {
2956 ERRARG("client_name");
2957 return -1;
2958 } else if (!session_clb) {
2959 ERRARG("session_clb");
2960 return -1;
2961 }
2962
2963 arg = malloc(sizeof *arg);
2964 if (!arg) {
2965 ERRMEM;
2966 return -1;
2967 }
2968 arg->client_name = strdup(client_name);
2969 if (!arg->client_name) {
2970 ERRMEM;
2971 free(arg);
2972 return -1;
2973 }
2974 arg->session_clb = session_clb;
2975
2976 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2977 if (ret) {
2978 ERR("Creating a new thread failed (%s).", strerror(ret));
2979 free(arg->client_name);
2980 free(arg);
2981 return -1;
2982 }
2983 /* the thread now manages arg */
2984
2985 pthread_detach(tid);
2986
2987 return 0;
2988}
2989
Radek Krejci53691be2016-02-22 13:58:37 +01002990#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002991
Michal Vaskoe8e07702017-03-15 10:19:30 +01002992API int
2993nc_server_endpt_count(void)
2994{
2995 return server_opts.endpt_count;
2996}
2997
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002998API time_t
2999nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003000{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003001 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003002 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003003 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003004 }
3005
Michal Vasko2e6defd2016-10-07 15:48:15 +02003006 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003007}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003008
3009API void
3010nc_session_set_notif_status(struct nc_session *session, int notif_status)
3011{
3012 if (!session || (session->side != NC_SERVER)) {
3013 ERRARG("session");
3014 return;
3015 }
3016
3017 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3018}
3019
3020API int
3021nc_session_get_notif_status(const struct nc_session *session)
3022{
3023 if (!session || (session->side != NC_SERVER)) {
3024 ERRARG("session");
3025 return 0;
3026 }
3027
3028 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003029}