blob: ba708c371ed4f2f26c765806a3fee2236325de93 [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 */
Michal Vaskoade892d2017-02-22 13:40:35 +010014#define _POSIX_SOUCE /* signals */
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
129 session->term_reason = reason;
130}
131
Michal Vasko086311b2016-01-08 09:53:11 +0100132int
Michal Vaskof05562c2016-01-20 12:06:43 +0100133nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100134{
135 const int optVal = 1;
136 const socklen_t optLen = sizeof(optVal);
137 int is_ipv4, sock;
138 struct sockaddr_storage saddr;
139
140 struct sockaddr_in *saddr4;
141 struct sockaddr_in6 *saddr6;
142
143
144 if (!strchr(address, ':')) {
145 is_ipv4 = 1;
146 } else {
147 is_ipv4 = 0;
148 }
149
150 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
151 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100152 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100153 goto fail;
154 }
155
156 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100157 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100158 goto fail;
159 }
160
161 bzero(&saddr, sizeof(struct sockaddr_storage));
162 if (is_ipv4) {
163 saddr4 = (struct sockaddr_in *)&saddr;
164
165 saddr4->sin_family = AF_INET;
166 saddr4->sin_port = htons(port);
167
168 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100169 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100170 goto fail;
171 }
172
173 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100174 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100175 goto fail;
176 }
177
178 } else {
179 saddr6 = (struct sockaddr_in6 *)&saddr;
180
181 saddr6->sin6_family = AF_INET6;
182 saddr6->sin6_port = htons(port);
183
184 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100185 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100186 goto fail;
187 }
188
189 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100190 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100191 goto fail;
192 }
193 }
194
Michal Vaskofb89d772016-01-08 12:25:35 +0100195 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100196 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100197 goto fail;
198 }
199
200 return sock;
201
202fail:
203 if (sock > -1) {
204 close(sock);
205 }
206
207 return -1;
208}
209
210int
Michal Vasko3031aae2016-01-27 16:07:18 +0100211nc_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 +0100212{
Michal Vaskof54cd352017-02-22 13:42:02 +0100213 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100214 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100215 struct pollfd *pfd;
216 struct sockaddr_storage saddr;
217 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100218 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100219
220 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100221 if (!pfd) {
222 ERRMEM;
223 return -1;
224 }
225
Michal Vaskoac2f6182017-01-30 14:32:03 +0100226 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200227 if (binds[i].sock < 0) {
228 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200229 continue;
230 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200231 if (binds[i].pollin) {
232 binds[i].pollin = 0;
233 /* leftover pollin */
234 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100235 break;
236 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100237 pfd[pfd_count].fd = binds[i].sock;
238 pfd[pfd_count].events = POLLIN;
239 pfd[pfd_count].revents = 0;
240
241 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100242 }
243
Michal Vasko0a3f3752016-10-13 14:58:38 +0200244 if (sock == -1) {
245 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100246 sigfillset(&sigmask);
247 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100248 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100249 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
250
Michal Vasko0a3f3752016-10-13 14:58:38 +0200251 if (!ret) {
252 /* we timeouted */
253 free(pfd);
254 return 0;
255 } else if (ret == -1) {
256 ERR("Poll failed (%s).", strerror(errno));
257 free(pfd);
258 return -1;
259 }
Michal Vasko086311b2016-01-08 09:53:11 +0100260
Michal Vaskoac2f6182017-01-30 14:32:03 +0100261 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
262 /* adjust i so that indices in binds and pfd always match */
263 while (binds[i].sock != pfd[j].fd) {
264 ++i;
265 }
266
267 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200268 --ret;
269
270 if (!ret) {
271 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100272 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200273 break;
274 } else {
275 /* just remember the event for next time */
276 binds[i].pollin = 1;
277 }
278 }
Michal Vasko086311b2016-01-08 09:53:11 +0100279 }
280 }
281 free(pfd);
282
283 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100284 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100285 return -1;
286 }
287
288 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100289 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100290 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100291 return -1;
292 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200293 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100294
Michal Vasko0190bc32016-03-02 15:47:49 +0100295 /* make the socket non-blocking */
296 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
297 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100298 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100299 return -1;
300 }
301
Michal Vasko3031aae2016-01-27 16:07:18 +0100302 if (idx) {
303 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100304 }
305
Michal Vasko086311b2016-01-08 09:53:11 +0100306 /* host was requested */
307 if (host) {
308 if (saddr.ss_family == AF_INET) {
309 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100310 if (*host) {
311 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
312 ERR("inet_ntop failed (%s).", strerror(errno));
313 free(*host);
314 *host = NULL;
315 }
Michal Vasko086311b2016-01-08 09:53:11 +0100316
Michal Vasko4eb3c312016-03-01 14:09:37 +0100317 if (port) {
318 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
319 }
320 } else {
321 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100322 }
323 } else if (saddr.ss_family == AF_INET6) {
324 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100325 if (*host) {
326 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
327 ERR("inet_ntop failed (%s).", strerror(errno));
328 free(*host);
329 *host = NULL;
330 }
Michal Vasko086311b2016-01-08 09:53:11 +0100331
Michal Vasko4eb3c312016-03-01 14:09:37 +0100332 if (port) {
333 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
334 }
335 } else {
336 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100337 }
338 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100339 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100340 }
341 }
342
343 return ret;
344}
345
Michal Vasko05ba9df2016-01-13 14:40:27 +0100346static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100347nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100348{
349 const char *identifier = NULL, *version = NULL, *format = NULL;
350 char *model_data = NULL;
351 const struct lys_module *module;
352 struct nc_server_error *err;
353 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100354 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100355
356 LY_TREE_FOR(rpc->child, child) {
357 if (!strcmp(child->schema->name, "identifier")) {
358 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
359 } else if (!strcmp(child->schema->name, "version")) {
360 version = ((struct lyd_node_leaf_list *)child)->value_str;
Radek Krejci1afa7792017-03-26 11:24:16 -0500361 if (version && version[0] == '\0') {
362 version = NULL;
363 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100364 } else if (!strcmp(child->schema->name, "format")) {
365 format = ((struct lyd_node_leaf_list *)child)->value_str;
366 }
367 }
368
369 /* check version */
370 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100371 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
372 nc_err_set_msg(err, "The requested version is not supported.", "en");
373 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100374 }
375
376 /* check and get module with the name identifier */
377 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
378 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200379 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
380 }
381 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100382 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
383 nc_err_set_msg(err, "The requested schema was not found.", "en");
384 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100385 }
386
387 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100388 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100389 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
Radek Krejci90fba642016-12-07 15:59:45 +0100390 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100391 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
392 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100393 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
394 nc_err_set_msg(err, "The requested format is not supported.", "en");
395 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100396 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200397 if (!model_data) {
398 ERRINT;
399 return NULL;
400 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100401
Michal Vasko303245c2016-03-24 15:20:03 +0100402 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200403 if (!sdata) {
404 ERRINT;
405 free(model_data);
406 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100407 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200408
Radek Krejci539efb62016-08-24 15:05:16 +0200409 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
410 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100411 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100412 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200413 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100414 return NULL;
415 }
416
Radek Krejci36dfdb32016-09-01 16:56:35 +0200417 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100418}
419
420static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100421nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100422{
Michal Vasko428087d2016-01-14 16:04:28 +0100423 session->term_reason = NC_SESSION_TERM_CLOSED;
424 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100425}
426
Michal Vasko086311b2016-01-08 09:53:11 +0100427API int
428nc_server_init(struct ly_ctx *ctx)
429{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100430 const struct lys_node *rpc;
431
Michal Vasko086311b2016-01-08 09:53:11 +0100432 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200433 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100434 return -1;
435 }
436
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100437 nc_init();
438
Michal Vasko05ba9df2016-01-13 14:40:27 +0100439 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100440 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vaskofd100c92016-03-01 15:23:46 +0100441 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100442 lys_set_private(rpc, nc_clb_default_get_schema);
443 }
444
445 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100446 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100447 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100448 lys_set_private(rpc, nc_clb_default_close_session);
449 }
450
Michal Vasko086311b2016-01-08 09:53:11 +0100451 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100452
453 server_opts.new_session_id = 1;
454 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
455
Michal Vasko086311b2016-01-08 09:53:11 +0100456 return 0;
457}
458
Michal Vaskob48aa812016-01-18 14:13:09 +0100459API void
460nc_server_destroy(void)
461{
Radek Krejci658782b2016-12-04 22:04:55 +0100462 unsigned int i;
463
464 for (i = 0; i < server_opts.capabilities_count; i++) {
465 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
466 }
467 free(server_opts.capabilities);
Michal Vaskob48aa812016-01-18 14:13:09 +0100468 pthread_spin_destroy(&server_opts.sid_lock);
469
Radek Krejci53691be2016-02-22 13:58:37 +0100470#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100471 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100472#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100473#ifdef NC_ENABLED_SSH
474 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100475
476 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
477 server_opts.hostkey_data_free(server_opts.hostkey_data);
478 }
479#endif
480#ifdef NC_ENABLED_TLS
481 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
482 server_opts.server_cert_data_free(server_opts.server_cert_data);
483 }
484 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
485 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
486 }
Michal Vaskob48aa812016-01-18 14:13:09 +0100487#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100488 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100489}
490
Michal Vasko086311b2016-01-08 09:53:11 +0100491API int
492nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
493{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200494 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
495 ERRARG("basic_mode");
496 return -1;
497 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
498 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100499 return -1;
500 }
501
502 server_opts.wd_basic_mode = basic_mode;
503 server_opts.wd_also_supported = also_supported;
504 return 0;
505}
506
Michal Vasko1a38c862016-01-15 15:50:07 +0100507API void
Michal Vasko55f03972016-04-13 08:56:01 +0200508nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
509{
510 if (!basic_mode && !also_supported) {
511 ERRARG("basic_mode and also_supported");
512 return;
513 }
514
515 if (basic_mode) {
516 *basic_mode = server_opts.wd_basic_mode;
517 }
518 if (also_supported) {
519 *also_supported = server_opts.wd_also_supported;
520 }
521}
522
Michal Vasko55f03972016-04-13 08:56:01 +0200523API int
Radek Krejci658782b2016-12-04 22:04:55 +0100524nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200525{
Radek Krejci658782b2016-12-04 22:04:55 +0100526 const char **new;
527
528 if (!value || !value[0]) {
529 ERRARG("value must not be empty");
530 return EXIT_FAILURE;
531 }
532
533 server_opts.capabilities_count++;
534 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
535 if (!new) {
536 ERRMEM;
537 return EXIT_FAILURE;
538 }
539 server_opts.capabilities = new;
540 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
541
542 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200543}
544
Michal Vasko1a38c862016-01-15 15:50:07 +0100545API void
Michal Vasko086311b2016-01-08 09:53:11 +0100546nc_server_set_hello_timeout(uint16_t hello_timeout)
547{
Michal Vasko086311b2016-01-08 09:53:11 +0100548 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100549}
550
Michal Vasko55f03972016-04-13 08:56:01 +0200551API uint16_t
552nc_server_get_hello_timeout(void)
553{
554 return server_opts.hello_timeout;
555}
556
Michal Vasko1a38c862016-01-15 15:50:07 +0100557API void
Michal Vasko086311b2016-01-08 09:53:11 +0100558nc_server_set_idle_timeout(uint16_t idle_timeout)
559{
Michal Vasko086311b2016-01-08 09:53:11 +0100560 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100561}
562
Michal Vasko55f03972016-04-13 08:56:01 +0200563API uint16_t
564nc_server_get_idle_timeout(void)
565{
566 return server_opts.idle_timeout;
567}
568
Michal Vasko71090fc2016-05-24 16:37:28 +0200569API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100570nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100571{
Michal Vasko71090fc2016-05-24 16:37:28 +0200572 NC_MSG_TYPE msgtype;
573
Michal Vasko45e53ae2016-04-07 11:46:03 +0200574 if (!server_opts.ctx) {
575 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200576 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200577 } else if (fdin < 0) {
578 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200579 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200580 } else if (fdout < 0) {
581 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200582 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200583 } else if (!username) {
584 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200585 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200586 } else if (!session) {
587 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200588 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100589 }
590
591 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +0100592 *session = nc_new_session(0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100593 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100594 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200595 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100596 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100597 (*session)->status = NC_STATUS_STARTING;
598 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100599
Michal Vaskoade892d2017-02-22 13:40:35 +0100600 /* transport lock */
601 pthread_mutex_init((*session)->ti_lock, NULL);
602 pthread_cond_init((*session)->ti_cond, NULL);
603 *(*session)->ti_inuse = 0;
604
Michal Vasko086311b2016-01-08 09:53:11 +0100605 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100606 (*session)->ti_type = NC_TI_FD;
607 (*session)->ti.fd.in = fdin;
608 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100609
610 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100611 (*session)->flags = NC_SESSION_SHAREDCTX;
612 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100613
Michal Vaskob48aa812016-01-18 14:13:09 +0100614 /* assign new SID atomically */
615 pthread_spin_lock(&server_opts.sid_lock);
616 (*session)->id = server_opts.new_session_id++;
617 pthread_spin_unlock(&server_opts.sid_lock);
618
Michal Vasko086311b2016-01-08 09:53:11 +0100619 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200620 msgtype = nc_handshake(*session);
621 if (msgtype != NC_MSG_HELLO) {
622 nc_session_free(*session, NULL);
623 *session = NULL;
624 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100625 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200626 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100627 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100628
Michal Vasko71090fc2016-05-24 16:37:28 +0200629 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100630}
Michal Vasko9e036d52016-01-08 10:49:26 +0100631
Michal Vaskob30b99c2016-07-26 11:35:43 +0200632static void
633nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
634{
635 uint8_t i, found = 0;
636
637 for (i = 0; i < ps->queue_len; ++i) {
638 /* idx round buffer adjust */
639 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
640 i = -ps->queue_begin;
641 }
642
643 if (found) {
644 /* move the value back one place */
645 if (ps->queue[ps->queue_begin + i] == id) {
646 /* another equal value, simply cannot be */
647 ERRINT;
648 }
649
650 if (ps->queue_begin + i == 0) {
651 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
652 } else {
653 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
654 }
655 } else if (ps->queue[ps->queue_begin + i] == id) {
656 /* found our id, there can be no more equal valid values */
657 found = 1;
658 }
659 }
660
661 if (!found) {
662 ERRINT;
663 }
664 --ps->queue_len;
665}
666
Michal Vaskof04a52a2016-04-07 10:52:10 +0200667int
Michal Vasko26043172016-07-26 14:08:59 +0200668nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200669{
670 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200671 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200672 struct timespec ts;
673
Radek Krejci7ac16052016-07-15 11:48:18 +0200674 nc_gettimespec(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100675 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200676
677 /* LOCK */
678 ret = pthread_mutex_timedlock(&ps->lock, &ts);
679 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200680 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200681 return -1;
682 }
683
684 /* get a unique queue value (by adding 1 to the last added value, if any) */
685 if (ps->queue_len) {
686 queue_last = ps->queue_begin + ps->queue_len - 1;
687 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
688 queue_last -= NC_PS_QUEUE_SIZE;
689 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200690 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200691 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200692 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200693 }
694
695 /* add ourselves into the queue */
696 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200697 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200698 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200699 return -1;
700 }
701 ++ps->queue_len;
702 queue_last = ps->queue_begin + ps->queue_len - 1;
703 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
704 queue_last -= NC_PS_QUEUE_SIZE;
705 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200706 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200707
708 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200709 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200710 nc_gettimespec(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100711 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200712
713 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
714 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200715 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200716 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200717 nc_ps_queue_remove_id(ps, *id);
718 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200719 return -1;
720 }
721 }
722
Michal Vaskobe86fe32016-04-07 10:43:03 +0200723 /* UNLOCK */
724 pthread_mutex_unlock(&ps->lock);
725
726 return 0;
727}
728
Michal Vaskof04a52a2016-04-07 10:52:10 +0200729int
Michal Vasko26043172016-07-26 14:08:59 +0200730nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200731{
732 int ret;
733 struct timespec ts;
734
Radek Krejci7ac16052016-07-15 11:48:18 +0200735 nc_gettimespec(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100736 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200737
738 /* LOCK */
739 ret = pthread_mutex_timedlock(&ps->lock, &ts);
740 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200741 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200742 ret = -1;
743 }
744
Michal Vaskob30b99c2016-07-26 11:35:43 +0200745 /* we must be the first, it was our turn after all, right? */
746 if (ps->queue[ps->queue_begin] != id) {
747 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200748 /* UNLOCK */
749 if (!ret) {
750 pthread_mutex_unlock(&ps->lock);
751 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200752 return -1;
753 }
754
Michal Vaskobe86fe32016-04-07 10:43:03 +0200755 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200756 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200757
758 /* broadcast to all other threads that the queue moved */
759 pthread_cond_broadcast(&ps->cond);
760
Michal Vaskobe86fe32016-04-07 10:43:03 +0200761 /* UNLOCK */
762 if (!ret) {
763 pthread_mutex_unlock(&ps->lock);
764 }
765
766 return ret;
767}
768
Michal Vasko428087d2016-01-14 16:04:28 +0100769API struct nc_pollsession *
770nc_ps_new(void)
771{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100772 struct nc_pollsession *ps;
773
774 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100775 if (!ps) {
776 ERRMEM;
777 return NULL;
778 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200779 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100780 pthread_mutex_init(&ps->lock, NULL);
781
782 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100783}
784
785API void
786nc_ps_free(struct nc_pollsession *ps)
787{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100788 if (!ps) {
789 return;
790 }
791
Michal Vaskobe86fe32016-04-07 10:43:03 +0200792 if (ps->queue_len) {
793 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
794 }
795
Michal Vasko428087d2016-01-14 16:04:28 +0100796 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100797 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200798 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100799
Michal Vasko428087d2016-01-14 16:04:28 +0100800 free(ps);
801}
802
803API int
804nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
805{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200806 uint8_t q_id;
807
Michal Vasko45e53ae2016-04-07 11:46:03 +0200808 if (!ps) {
809 ERRARG("ps");
810 return -1;
811 } else if (!session) {
812 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100813 return -1;
814 }
815
Michal Vasko48a63ed2016-03-01 09:48:21 +0100816 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200817 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200818 return -1;
819 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100820
Michal Vasko428087d2016-01-14 16:04:28 +0100821 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100822 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +0100823 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100824 ERRMEM;
825 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200826 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100827 return -1;
828 }
Michal Vasko3a715132016-01-21 15:40:31 +0100829 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100830
Michal Vasko48a63ed2016-03-01 09:48:21 +0100831 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200832 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100833}
834
Michal Vasko48a63ed2016-03-01 09:48:21 +0100835static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100836_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100837{
838 uint16_t i;
839
Radek Krejcid5f978f2016-03-03 13:14:45 +0100840 if (index >= 0) {
841 i = (uint16_t)index;
842 goto remove;
843 }
Michal Vasko428087d2016-01-14 16:04:28 +0100844 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100845 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100846remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100847 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100848 if (i < ps->session_count) {
849 ps->sessions[i] = ps->sessions[ps->session_count];
Michal Vasko58005732016-02-02 15:50:52 +0100850 } else if (!ps->session_count) {
851 free(ps->sessions);
852 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +0100853 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +0100854 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100855 return 0;
856 }
857 }
858
Michal Vaskof0537d82016-01-29 14:42:38 +0100859 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100860}
861
Michal Vasko48a63ed2016-03-01 09:48:21 +0100862API int
863nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
864{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200865 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200866 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100867
Michal Vasko45e53ae2016-04-07 11:46:03 +0200868 if (!ps) {
869 ERRARG("ps");
870 return -1;
871 } else if (!session) {
872 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100873 return -1;
874 }
875
876 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200877 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200878 return -1;
879 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100880
Radek Krejcid5f978f2016-03-03 13:14:45 +0100881 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100882
883 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200884 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100885
Michal Vaskobe86fe32016-04-07 10:43:03 +0200886 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100887}
888
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100889API struct nc_session *
890nc_ps_get_session_by_sid(const struct nc_pollsession *ps, uint32_t sid)
891{
892 uint8_t q_id;
893 uint16_t i;
894 struct nc_session *ret = NULL;
895
896 if (!ps) {
897 ERRARG("ps");
898 return NULL;
899 }
900
901 /* LOCK */
902 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
903 return NULL;
904 }
905
906 for (i = 0; i < ps->session_count; ++i) {
907 if (ps->sessions[i]->id == sid) {
908 ret = ps->sessions[i];
909 break;
910 }
911 }
912
913 /* UNLOCK */
914 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
915
916 return ret;
917}
918
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100919API uint16_t
920nc_ps_session_count(struct nc_pollsession *ps)
921{
922 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200923 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100924 return 0;
925 }
926
Michal Vaskof4462fd2017-02-15 14:29:05 +0100927 return ps->session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100928}
929
Michal Vasko71090fc2016-05-24 16:37:28 +0200930/* must be called holding the session lock!
931 * returns: NC_PSPOLL_ERROR,
932 * NC_PSPOLL_BAD_RPC,
933 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
934 * NC_PSPOLL_RPC
935 */
936static int
Radek Krejci93e80222016-10-03 13:34:25 +0200937nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100938{
939 struct lyxml_elem *xml = NULL;
940 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200941 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200942 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100943
Michal Vasko45e53ae2016-04-07 11:46:03 +0200944 if (!session) {
945 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200946 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200947 } else if (!rpc) {
948 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200949 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100950 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100951 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200952 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100953 }
954
955 msgtype = nc_read_msg(session, &xml);
956
957 switch (msgtype) {
958 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200959 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100960 if (!*rpc) {
961 ERRMEM;
962 goto error;
963 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100964
Radek Krejcif93c7d42016-04-06 13:41:15 +0200965 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +0100966 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
967 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100968 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200969 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200970 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200971 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200972 nc_server_reply_free(reply);
973 if (ret == -1) {
974 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200975 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200976 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
977 } else {
978 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100979 }
Michal Vasko428087d2016-01-14 16:04:28 +0100980 (*rpc)->root = xml;
981 break;
982 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100983 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200984 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100985 goto error;
986 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100987 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200988 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100989 goto error;
990 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100991 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200992 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100993 goto error;
994 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200995 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100996 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
997 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200998 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100999 break;
1000 }
1001
Michal Vasko71090fc2016-05-24 16:37:28 +02001002 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001003
1004error:
1005 /* cleanup */
1006 lyxml_free(server_opts.ctx, xml);
1007
Michal Vasko71090fc2016-05-24 16:37:28 +02001008 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001009}
1010
fanchanghu966f2de2016-07-21 02:28:57 -04001011API void
1012nc_set_global_rpc_clb(nc_rpc_clb clb)
1013{
1014 global_rpc_clb = clb;
1015}
1016
Radek Krejci93e80222016-10-03 13:34:25 +02001017API NC_MSG_TYPE
1018nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1019{
1020 NC_MSG_TYPE result = NC_MSG_NOTIF;
1021 int ret;
1022
1023 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001024 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001025 ERRARG("session");
1026 return NC_MSG_ERROR;
1027 } else if (!notif || !notif->tree || !notif->eventtime) {
1028 ERRARG("notif");
1029 return NC_MSG_ERROR;
1030 }
1031
1032 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vaskoade892d2017-02-22 13:40:35 +01001033 ret = nc_session_lock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001034 if (ret < 0) {
1035 return NC_MSG_ERROR;
1036 } else if (!ret) {
1037 return NC_MSG_WOULDBLOCK;
1038 }
1039
1040 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1041 if (ret == -1) {
1042 ERR("Session %u: failed to write notification.", session->id);
1043 result = NC_MSG_ERROR;
1044 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001045
1046 nc_session_unlock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001047
1048 return result;
1049}
1050
Michal Vasko71090fc2016-05-24 16:37:28 +02001051/* must be called holding the session lock!
1052 * returns: NC_PSPOLL_ERROR,
1053 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1054 * NC_PSPOLL_REPLY_ERROR,
1055 * 0
1056 */
1057static int
Radek Krejci93e80222016-10-03 13:34:25 +02001058nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001059{
1060 nc_rpc_clb clb;
1061 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001062 struct lys_node *rpc_act = NULL;
1063 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001064 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001065
Michal Vasko4a827e52016-03-03 10:59:00 +01001066 if (!rpc) {
1067 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001068 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001069 }
1070
Michal Vasko90e8e692016-07-13 12:27:57 +02001071 if (rpc->tree->schema->nodetype == LYS_RPC) {
1072 /* RPC */
1073 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001074 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001075 /* action */
1076 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1077 if (elem->schema->nodetype == LYS_ACTION) {
1078 rpc_act = elem->schema;
1079 break;
1080 }
1081 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001082 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001083 if (!rpc_act) {
1084 ERRINT;
1085 return NC_PSPOLL_ERROR;
1086 }
1087 }
1088
1089 if (!rpc_act->priv) {
1090 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001091 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001092 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001093 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001094 reply = clb(rpc->tree, session);
1095 }
1096
1097 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001098 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001099 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001100 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1101 if (reply->type == NC_RPL_ERROR) {
1102 ret |= NC_PSPOLL_REPLY_ERROR;
1103 }
1104 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001105
Michal Vasko71090fc2016-05-24 16:37:28 +02001106 if (r == -1) {
1107 ERR("Session %u: failed to write reply.", session->id);
1108 ret |= NC_PSPOLL_ERROR;
1109 }
Michal Vasko428087d2016-01-14 16:04:28 +01001110
1111 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1112 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1113 session->status = NC_STATUS_INVALID;
1114 }
1115
Michal Vasko71090fc2016-05-24 16:37:28 +02001116 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001117}
1118
1119API int
Michal Vasko71090fc2016-05-24 16:37:28 +02001120nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +01001121{
Michal Vasko9a327362017-01-11 11:31:46 +01001122 int ret, r, poll_ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001123 uint8_t q_id;
Michal Vasko9a327362017-01-11 11:31:46 +01001124 uint16_t i, j;
1125 char msg[256];
1126 NC_SESSION_TERM_REASON term_reason;
1127 struct pollfd pfd;
Michal Vasko36c7be82017-02-22 13:37:59 +01001128 struct timespec ts_timeout, ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +02001129 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +01001130 struct nc_server_rpc *rpc = NULL;
Michal Vasko9a327362017-01-11 11:31:46 +01001131#ifdef NC_ENABLED_SSH
1132 struct nc_session *new;
1133#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001134
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001135 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001136 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001137 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001138 }
1139
Michal Vaskoade892d2017-02-22 13:40:35 +01001140 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001141 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001142 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001143 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001144
Michal Vaskoade892d2017-02-22 13:40:35 +01001145 if (!ps->session_count) {
1146 ret = NC_PSPOLL_NOSESSIONS;
1147 goto ps_unlock_finish;
1148 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001149
Michal Vasko36c7be82017-02-22 13:37:59 +01001150 /* check timeout of all the sessions */
1151 nc_gettimespec(&ts_cur);
1152 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko1ff97762017-05-19 10:45:45 +02001153 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1154 /* when the status change occurred an error was printed, no need to print another */
1155 if (!(ps->sessions[i]->flags & NC_SESSION_CALLHOME)) {
1156 /* ... so the application should have handled it and removed this session before unless
1157 * this is a Call Home session, when it could not */
1158 WRN("Session %u: polling an invalid session.", ps->sessions[i]->id);
1159 }
1160 ret = NC_PSPOLL_SESSION_TERM;
1161 if (ps->sessions[i]->term_reason != NC_SESSION_TERM_CLOSED) {
1162 ret |= NC_PSPOLL_SESSION_ERROR;
1163 }
1164 if (session) {
1165 *session = ps->sessions[i];
1166 }
1167 goto ps_unlock_finish;
1168 } else if (!(ps->sessions[i]->flags & NC_SESSION_CALLHOME) && !ps->sessions[i]->opts.server.ntf_status
Michal Vasko3486a7c2017-03-03 13:28:07 +01001169 && server_opts.idle_timeout
Michal Vasko36c7be82017-02-22 13:37:59 +01001170 && (ts_cur.tv_sec >= ps->sessions[i]->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001171 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1172 ps->sessions[i]->status = NC_STATUS_INVALID;
1173 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001174 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1175 if (session) {
1176 *session = ps->sessions[i];
1177 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001178 goto ps_unlock_finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001179 }
Michal Vasko428087d2016-01-14 16:04:28 +01001180 }
1181
Michal Vasko36c7be82017-02-22 13:37:59 +01001182 if (timeout > -1) {
1183 nc_gettimespec(&ts_timeout);
1184 nc_addtimespec(&ts_timeout, timeout);
1185 }
1186
1187 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001188 do {
1189 /* loop from i to j */
1190 if (ps->last_event_session == ps->session_count - 1) {
1191 i = j = 0;
1192 } else {
1193 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001194 }
Michal Vasko9a327362017-01-11 11:31:46 +01001195 do {
1196 cur_session = ps->sessions[i];
Michal Vasko428087d2016-01-14 16:04:28 +01001197
Michal Vaskoade892d2017-02-22 13:40:35 +01001198 /* SESSION LOCK */
1199 if ((cur_session->status == NC_STATUS_RUNNING)
1200 && !*cur_session->ti_inuse && ((r = nc_session_lock(cur_session, 0, __func__)))) {
1201 /* we go here if we successfully lock the session or there was an error, on timeout we simply skip it */
1202 if (r == -1) {
Michal Vasko9a327362017-01-11 11:31:46 +01001203 ret = NC_PSPOLL_ERROR;
Michal Vaskoade892d2017-02-22 13:40:35 +01001204 goto ps_unlock_finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001205 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001206 /* damn race condition */
1207 if (cur_session->status != NC_STATUS_RUNNING) {
1208 /* SESSION UNLOCK */
1209 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
1210 goto next_iteration;
1211 }
Michal Vasko9a327362017-01-11 11:31:46 +01001212
Michal Vaskoade892d2017-02-22 13:40:35 +01001213 switch (cur_session->ti_type) {
Michal Vasko428087d2016-01-14 16:04:28 +01001214#ifdef NC_ENABLED_SSH
Michal Vaskoade892d2017-02-22 13:40:35 +01001215 case NC_TI_LIBSSH:
1216 r = ssh_channel_poll_timeout(cur_session->ti.libssh.channel, 0, 0);
1217 if (r < 1) {
1218 if (r == SSH_EOF) {
1219 sprintf(msg, "SSH channel unexpected EOF");
1220 term_reason = NC_SESSION_TERM_DROPPED;
1221 poll_ret = -2;
1222 } else if (r == SSH_ERROR) {
1223 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(cur_session->ti.libssh.session));
1224 term_reason = NC_SESSION_TERM_OTHER;
1225 poll_ret = -2;
1226 } else {
1227 poll_ret = 0;
1228 }
1229 break;
1230 }
1231
1232 /* we have some data, but it may be just an SSH message */
1233 r = ssh_execute_message_callbacks(cur_session->ti.libssh.session);
1234 if (r != SSH_OK) {
1235 sprintf(msg, "failed to receive SSH messages (%s)", ssh_get_error(cur_session->ti.libssh.session));
Michal Vasko76be6752017-01-19 12:14:48 +01001236 term_reason = NC_SESSION_TERM_OTHER;
1237 poll_ret = -2;
Michal Vaskoade892d2017-02-22 13:40:35 +01001238 } else if (cur_session->flags & NC_SESSION_SSH_NEW_MSG) {
1239 /* new SSH message */
1240 cur_session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1241 if (cur_session->ti.libssh.next) {
1242 for (new = cur_session->ti.libssh.next; new != cur_session; new = new->ti.libssh.next) {
1243 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1244 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1245 /* new NETCONF SSH channel */
1246 if (session) {
1247 *session = cur_session;
1248 }
1249 ret = NC_PSPOLL_SSH_CHANNEL;
1250 goto session_ps_unlock_finish;
Michal Vasko9a327362017-01-11 11:31:46 +01001251 }
Michal Vasko9a327362017-01-11 11:31:46 +01001252 }
1253 }
Michal Vasko9a327362017-01-11 11:31:46 +01001254
Michal Vaskoade892d2017-02-22 13:40:35 +01001255 /* just some SSH message */
1256 if (session) {
1257 *session = cur_session;
1258 }
1259 ret = NC_PSPOLL_SSH_MSG;
1260 goto session_ps_unlock_finish;
1261 } else {
1262 /* we have some application data */
1263 poll_ret = 1;
Michal Vasko9a327362017-01-11 11:31:46 +01001264 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001265 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001266#endif
1267#ifdef NC_ENABLED_TLS
Michal Vaskoade892d2017-02-22 13:40:35 +01001268 case NC_TI_OPENSSL:
1269 r = SSL_pending(cur_session->ti.tls);
1270 if (!r) {
1271 /* no data pending in the SSL buffer, poll fd */
1272 pfd.fd = SSL_get_rfd(cur_session->ti.tls);
1273 if (pfd.fd < 0) {
1274 ERRINT;
1275 ret = NC_PSPOLL_ERROR;
1276 goto session_ps_unlock_finish;
1277 }
1278 pfd.events = POLLIN;
1279 pfd.revents = 0;
1280 r = poll(&pfd, 1, 0);
1281
1282 if ((r < 0) && (errno != EINTR)) {
1283 sprintf(msg, "poll failed (%s)", strerror(errno));
1284 poll_ret = -1;
1285 } else if (r > 0) {
1286 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1287 sprintf(msg, "communication socket unexpectedly closed");
1288 term_reason = NC_SESSION_TERM_DROPPED;
1289 poll_ret = -2;
1290 } else if (pfd.revents & POLLERR) {
1291 sprintf(msg, "communication socket error");
1292 term_reason = NC_SESSION_TERM_OTHER;
1293 poll_ret = -2;
1294 } else {
1295 poll_ret = 1;
1296 }
1297 } else {
1298 poll_ret = 0;
1299 }
1300 } else {
1301 poll_ret = 1;
Michal Vasko9a327362017-01-11 11:31:46 +01001302 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001303 break;
1304#endif
1305 case NC_TI_FD:
1306 pfd.fd = cur_session->ti.fd.in;
Michal Vasko9a327362017-01-11 11:31:46 +01001307 pfd.events = POLLIN;
1308 pfd.revents = 0;
1309 r = poll(&pfd, 1, 0);
1310
Michal Vaskoade892d2017-02-22 13:40:35 +01001311 if ((r < 0) && (errno != EINTR)) {
Michal Vasko9a327362017-01-11 11:31:46 +01001312 sprintf(msg, "poll failed (%s)", strerror(errno));
1313 poll_ret = -1;
1314 } else if (r > 0) {
1315 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1316 sprintf(msg, "communication socket unexpectedly closed");
1317 term_reason = NC_SESSION_TERM_DROPPED;
1318 poll_ret = -2;
1319 } else if (pfd.revents & POLLERR) {
1320 sprintf(msg, "communication socket error");
1321 term_reason = NC_SESSION_TERM_OTHER;
1322 poll_ret = -2;
1323 } else {
1324 poll_ret = 1;
1325 }
1326 } else {
1327 poll_ret = 0;
1328 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001329 break;
1330 case NC_TI_NONE:
1331 ERRINT;
1332 ret = NC_PSPOLL_ERROR;
1333 goto session_ps_unlock_finish;
Michal Vasko9a327362017-01-11 11:31:46 +01001334 }
Michal Vasko4d13eca2017-05-03 16:05:11 +02001335 /* we have some data, but it may be just an SSH message */
Michal Vaskoade892d2017-02-22 13:40:35 +01001336 /* here: poll_ret == -2 - session error, session terminated,
1337 * poll_ret == -1 - generic error,
1338 * poll_ret == 0 - nothing to read,
1339 * poll_ret > 0 - data available
1340 */
1341 if (poll_ret == -2) {
1342 ERR("Session %u: %s.", cur_session->id, msg);
1343 cur_session->status = NC_STATUS_INVALID;
1344 cur_session->term_reason = term_reason;
1345 if (session) {
1346 *session = cur_session;
Michal Vasko9a327362017-01-11 11:31:46 +01001347 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001348 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1349 goto session_ps_unlock_finish;
1350 } else if (poll_ret == -1) {
1351 ERR("Session %u: %s.", cur_session->id, msg);
1352 ret = NC_PSPOLL_ERROR;
1353 goto session_ps_unlock_finish;
1354 } else if (poll_ret > 0) {
1355 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001356 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001357
1358 /* SESSION UNLOCK */
1359 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
1360 } else {
1361 /* timeout */
1362 poll_ret = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001363 }
Michal Vasko428087d2016-01-14 16:04:28 +01001364
Michal Vaskoade892d2017-02-22 13:40:35 +01001365next_iteration:
Michal Vasko9a327362017-01-11 11:31:46 +01001366 if (i == ps->session_count - 1) {
1367 i = 0;
1368 } else {
1369 ++i;
1370 }
1371 } while (i != j);
1372
Michal Vaskoade892d2017-02-22 13:40:35 +01001373 /* no event, no session remains locked */
Michal Vasko9a327362017-01-11 11:31:46 +01001374 if (!poll_ret && (timeout > -1)) {
1375 usleep(NC_TIMEOUT_STEP);
1376
Michal Vaskoade892d2017-02-22 13:40:35 +01001377 nc_gettimespec(&ts_cur);
Michal Vasko9a327362017-01-11 11:31:46 +01001378 /* final timeout */
Michal Vaskoade892d2017-02-22 13:40:35 +01001379 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
Michal Vasko9a327362017-01-11 11:31:46 +01001380 ret = NC_PSPOLL_TIMEOUT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001381 goto ps_unlock_finish;
Michal Vasko9a327362017-01-11 11:31:46 +01001382 }
Michal Vasko428087d2016-01-14 16:04:28 +01001383 }
Michal Vasko9a327362017-01-11 11:31:46 +01001384 } while (!poll_ret);
Michal Vasko428087d2016-01-14 16:04:28 +01001385
Michal Vaskoade892d2017-02-22 13:40:35 +01001386 /* this is the session with some data available for reading, it is still locked */
Michal Vasko71090fc2016-05-24 16:37:28 +02001387 if (session) {
1388 *session = cur_session;
1389 }
Michal Vasko9a327362017-01-11 11:31:46 +01001390 ps->last_event_session = i;
Michal Vasko428087d2016-01-14 16:04:28 +01001391
Michal Vaskoade892d2017-02-22 13:40:35 +01001392 /* PS UNLOCK */
1393 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001394
Radek Krejci93e80222016-10-03 13:34:25 +02001395 ret = nc_server_recv_rpc(cur_session, &rpc);
Michal Vasko71090fc2016-05-24 16:37:28 +02001396 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001397 if (cur_session->status != NC_STATUS_RUNNING) {
1398 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001399 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001400 goto session_unlock_finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001401 }
1402
Michal Vasko2e6defd2016-10-07 15:48:15 +02001403 cur_session->opts.server.last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001404
Michal Vaskoade892d2017-02-22 13:40:35 +01001405 /* process RPC, not needed afterwards */
Radek Krejci93e80222016-10-03 13:34:25 +02001406 ret |= nc_server_send_reply(cur_session, rpc);
Michal Vaskoade892d2017-02-22 13:40:35 +01001407 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vasko428087d2016-01-14 16:04:28 +01001408
Michal Vasko71090fc2016-05-24 16:37:28 +02001409 if (cur_session->status != NC_STATUS_RUNNING) {
1410 ret |= NC_PSPOLL_SESSION_TERM;
1411 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1412 ret |= NC_PSPOLL_SESSION_ERROR;
1413 }
Michal Vasko428087d2016-01-14 16:04:28 +01001414 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001415
Michal Vaskoade892d2017-02-22 13:40:35 +01001416session_unlock_finish:
1417 /* SESSION UNLOCK */
1418 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
1419 return ret;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001420
Michal Vaskoade892d2017-02-22 13:40:35 +01001421session_ps_unlock_finish:
1422 /* SESSION UNLOCK */
1423 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
1424
1425ps_unlock_finish:
1426 /* PS UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001427 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001428 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001429}
1430
Michal Vaskod09eae62016-02-01 10:32:52 +01001431API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001432nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001433{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001434 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001435 uint16_t i;
1436 struct nc_session *session;
1437
Michal Vasko9a25e932016-02-01 10:36:42 +01001438 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001439 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001440 return;
1441 }
1442
Michal Vasko48a63ed2016-03-01 09:48:21 +01001443 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001444 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001445 return;
1446 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001447
Michal Vasko48a63ed2016-03-01 09:48:21 +01001448 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001449 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001450 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001451 }
1452 free(ps->sessions);
1453 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001454 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001455 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001456 } else {
1457 for (i = 0; i < ps->session_count; ) {
1458 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1459 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001460 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001461 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001462 continue;
1463 }
1464
1465 ++i;
1466 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001467 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001468
1469 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001470 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001471}
1472
Radek Krejci53691be2016-02-22 13:58:37 +01001473#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001474
Michal Vaskoe2713da2016-08-22 16:06:40 +02001475API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001476nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001477{
Michal Vasko3031aae2016-01-27 16:07:18 +01001478 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001479 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001480
Michal Vasko45e53ae2016-04-07 11:46:03 +02001481 if (!name) {
1482 ERRARG("name");
1483 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001484 }
1485
Michal Vaskoade892d2017-02-22 13:40:35 +01001486 /* BIND LOCK */
1487 pthread_mutex_lock(&server_opts.bind_lock);
1488
1489 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001490 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001491
1492 /* check name uniqueness */
1493 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001494 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001495 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001496 ret = -1;
1497 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001498 }
1499 }
1500
Michal Vasko3031aae2016-01-27 16:07:18 +01001501 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001502 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001503 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001504 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001505 ret = -1;
1506 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001507 }
1508 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001509 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001510
Michal Vaskoe2713da2016-08-22 16:06:40 +02001511 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001512 if (!server_opts.binds) {
1513 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001514 ret = -1;
1515 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001516 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001517
Michal Vasko2e6defd2016-10-07 15:48:15 +02001518 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1519 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1520 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001521 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001522
1523 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001524#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001525 case NC_TI_LIBSSH:
1526 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1527 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1528 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001529 ret = -1;
1530 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001531 }
1532 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1533 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1534 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1535 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1536 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001537#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001538#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001539 case NC_TI_OPENSSL:
1540 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1541 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1542 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001543 ret = -1;
1544 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001545 }
1546 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001547#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001548 default:
1549 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001550 ret = -1;
1551 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001552 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001553
Michal Vaskoade892d2017-02-22 13:40:35 +01001554cleanup:
1555 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001556 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001557
Michal Vaskoade892d2017-02-22 13:40:35 +01001558 /* BIND UNLOCK */
1559 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001560
Michal Vaskoade892d2017-02-22 13:40:35 +01001561 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001562}
1563
Michal Vasko3031aae2016-01-27 16:07:18 +01001564int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001565nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001566{
1567 struct nc_endpt *endpt;
1568 struct nc_bind *bind = NULL;
1569 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001570 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001571
Michal Vasko45e53ae2016-04-07 11:46:03 +02001572 if (!endpt_name) {
1573 ERRARG("endpt_name");
1574 return -1;
1575 } else if ((!address && !port) || (address && port)) {
1576 ERRARG("address and port");
1577 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001578 }
1579
Michal Vaskoe2713da2016-08-22 16:06:40 +02001580 if (address) {
1581 set_addr = 1;
1582 } else {
1583 set_addr = 0;
1584 }
1585
Michal Vaskoade892d2017-02-22 13:40:35 +01001586 /* BIND LOCK */
1587 pthread_mutex_lock(&server_opts.bind_lock);
1588
1589 /* ENDPT LOCK */
1590 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001591 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001592 /* BIND UNLOCK */
1593 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001594 return -1;
1595 }
1596
Michal Vaskoe2713da2016-08-22 16:06:40 +02001597 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001598
Michal Vaskoe2713da2016-08-22 16:06:40 +02001599 if (set_addr) {
1600 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001601 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001602 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001603 }
1604
Michal Vaskoe2713da2016-08-22 16:06:40 +02001605 /* we have all the information we need to create a listening socket */
1606 if (address && port) {
1607 /* create new socket, close the old one */
1608 sock = nc_sock_listen(address, port);
1609 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001610 ret = -1;
1611 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001612 }
1613
1614 if (bind->sock > -1) {
1615 close(bind->sock);
1616 }
1617 bind->sock = sock;
1618 } /* else we are just setting address or port */
1619
1620 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001621 lydict_remove(server_opts.ctx, bind->address);
1622 bind->address = lydict_insert(server_opts.ctx, address, 0);
1623 } else {
1624 bind->port = port;
1625 }
1626
Michal Vaskoe2713da2016-08-22 16:06:40 +02001627 if (sock > -1) {
1628#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001629 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001630#elif defined(NC_ENABLED_SSH)
1631 VRB("Listening on %s:%u for SSH connections.", address, port);
1632#else
1633 VRB("Listening on %s:%u for TLS connections.", address, port);
1634#endif
1635 }
1636
Michal Vasko4c1fb492017-01-30 14:31:07 +01001637cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001638 /* ENDPT UNLOCK */
1639 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001640
Michal Vaskoade892d2017-02-22 13:40:35 +01001641 /* BIND UNLOCK */
1642 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001643
Michal Vasko4c1fb492017-01-30 14:31:07 +01001644 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001645}
1646
Michal Vaskoe2713da2016-08-22 16:06:40 +02001647API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001648nc_server_endpt_set_address(const char *endpt_name, const char *address)
1649{
1650 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1651}
1652
1653API int
1654nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1655{
1656 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1657}
1658
1659API int
Michal Vasko59050372016-11-22 14:33:55 +01001660nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001661{
1662 uint32_t i;
1663 int ret = -1;
1664
Michal Vaskoade892d2017-02-22 13:40:35 +01001665 /* BIND LOCK */
1666 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001667
Michal Vaskoade892d2017-02-22 13:40:35 +01001668 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001669 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001670
Michal Vasko59050372016-11-22 14:33:55 +01001671 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001672 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001673 for (i = 0; i < server_opts.endpt_count; ++i) {
1674 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001675 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001676#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001677 case NC_TI_LIBSSH:
1678 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1679 free(server_opts.endpts[i].opts.ssh);
1680 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001681#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001682#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001683 case NC_TI_OPENSSL:
1684 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1685 free(server_opts.endpts[i].opts.tls);
1686 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001687#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001688 default:
1689 ERRINT;
1690 /* won't get here ...*/
1691 break;
1692 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001693 ret = 0;
1694 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001695 free(server_opts.endpts);
1696 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001697
1698 /* remove all binds */
1699 for (i = 0; i < server_opts.endpt_count; ++i) {
1700 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1701 if (server_opts.binds[i].sock > -1) {
1702 close(server_opts.binds[i].sock);
1703 }
1704 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001705 free(server_opts.binds);
1706 server_opts.binds = NULL;
1707
Michal Vasko3031aae2016-01-27 16:07:18 +01001708 server_opts.endpt_count = 0;
1709
Michal Vasko1a38c862016-01-15 15:50:07 +01001710 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001711 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001712 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001713 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001714 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001715 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001716 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001717#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001718 case NC_TI_LIBSSH:
1719 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1720 free(server_opts.endpts[i].opts.ssh);
1721 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001722#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001723#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001724 case NC_TI_OPENSSL:
1725 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1726 free(server_opts.endpts[i].opts.tls);
1727 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001728#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001729 default:
1730 ERRINT;
1731 break;
1732 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001733
Michal Vaskoe2713da2016-08-22 16:06:40 +02001734 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001735 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1736 if (server_opts.binds[i].sock > -1) {
1737 close(server_opts.binds[i].sock);
1738 }
1739
1740 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001741 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001742 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001743 free(server_opts.binds);
1744 server_opts.binds = NULL;
1745 free(server_opts.endpts);
1746 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001747 } else if (i < server_opts.endpt_count) {
1748 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1749 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001750 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001751
1752 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001753 if (name) {
1754 break;
1755 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001756 }
1757 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001758 }
1759
Michal Vaskoade892d2017-02-22 13:40:35 +01001760 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001761 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001762
Michal Vaskoade892d2017-02-22 13:40:35 +01001763 /* BIND UNLOCK */
1764 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001765
1766 return ret;
1767}
1768
Michal Vasko71090fc2016-05-24 16:37:28 +02001769API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001770nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001771{
Michal Vasko71090fc2016-05-24 16:37:28 +02001772 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001773 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001774 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001775 uint16_t port, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001776
Michal Vasko45e53ae2016-04-07 11:46:03 +02001777 if (!server_opts.ctx) {
1778 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001779 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001780 } else if (!session) {
1781 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001782 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001783 }
1784
Michal Vaskoade892d2017-02-22 13:40:35 +01001785 /* BIND LOCK */
1786 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001787
1788 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001789 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01001790 /* BIND UNLOCK */
1791 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001792 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001793 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001794
Michal Vaskoe2713da2016-08-22 16:06:40 +02001795 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001796 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001797 /* BIND UNLOCK */
1798 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001799 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001800 if (!ret) {
1801 return NC_MSG_WOULDBLOCK;
1802 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001803 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001804 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001805
1806 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
1807 /* ENDPT READ LOCK */
1808 pthread_rwlock_rdlock(&server_opts.endpt_lock);
1809
1810 /* BIND UNLOCK */
1811 pthread_mutex_unlock(&server_opts.bind_lock);
1812
Michal Vaskob48aa812016-01-18 14:13:09 +01001813 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001814
Michal Vaskoade892d2017-02-22 13:40:35 +01001815 *session = nc_new_session(0);
Michal Vasko686aa312016-01-21 15:58:18 +01001816 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001817 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001818 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001819 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001820 msgtype = NC_MSG_ERROR;
1821 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001822 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001823 (*session)->status = NC_STATUS_STARTING;
1824 (*session)->side = NC_SERVER;
1825 (*session)->ctx = server_opts.ctx;
1826 (*session)->flags = NC_SESSION_SHAREDCTX;
1827 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1828 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001829
1830 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001831 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001832 pthread_cond_init((*session)->ti_cond, NULL);
1833 *(*session)->ti_inuse = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001834
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001835 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001836#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001837 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1838 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001839 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001840 if (ret < 0) {
1841 msgtype = NC_MSG_ERROR;
1842 goto cleanup;
1843 } else if (!ret) {
1844 msgtype = NC_MSG_WOULDBLOCK;
1845 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001846 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001847 } else
1848#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001849#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001850 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1851 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001852 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001853 if (ret < 0) {
1854 msgtype = NC_MSG_ERROR;
1855 goto cleanup;
1856 } else if (!ret) {
1857 msgtype = NC_MSG_WOULDBLOCK;
1858 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001859 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001860 } else
1861#endif
1862 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001863 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001864 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001865 msgtype = NC_MSG_ERROR;
1866 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001867 }
1868
Michal Vasko2cc4c682016-03-01 09:16:48 +01001869 (*session)->data = NULL;
1870
Michal Vaskoade892d2017-02-22 13:40:35 +01001871 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001872 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001873
Michal Vaskob48aa812016-01-18 14:13:09 +01001874 /* assign new SID atomically */
1875 /* LOCK */
1876 pthread_spin_lock(&server_opts.sid_lock);
1877 (*session)->id = server_opts.new_session_id++;
1878 /* UNLOCK */
1879 pthread_spin_unlock(&server_opts.sid_lock);
1880
Michal Vasko9e036d52016-01-08 10:49:26 +01001881 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001882 msgtype = nc_handshake(*session);
1883 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001884 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001885 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001886 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001887 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001888 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001889 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001890
Michal Vasko71090fc2016-05-24 16:37:28 +02001891 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001892
Michal Vasko71090fc2016-05-24 16:37:28 +02001893cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001894 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001895 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001896
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001897 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001898 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001899 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001900}
1901
Michal Vasko2e6defd2016-10-07 15:48:15 +02001902API int
1903nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1904{
1905 uint16_t i;
1906
1907 if (!name) {
1908 ERRARG("name");
1909 return -1;
1910 } else if (!ti) {
1911 ERRARG("ti");
1912 return -1;
1913 }
1914
1915 /* WRITE LOCK */
1916 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1917
1918 /* check name uniqueness */
1919 for (i = 0; i < server_opts.ch_client_count; ++i) {
1920 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1921 ERR("Call Home client \"%s\" already exists.", name);
1922 /* WRITE UNLOCK */
1923 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1924 return -1;
1925 }
1926 }
1927
1928 ++server_opts.ch_client_count;
1929 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
1930 if (!server_opts.ch_clients) {
1931 ERRMEM;
1932 /* WRITE UNLOCK */
1933 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1934 return -1;
1935 }
1936 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1937 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001938 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
1939 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001940
1941 switch (ti) {
1942#ifdef NC_ENABLED_SSH
1943 case NC_TI_LIBSSH:
1944 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1945 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
1946 ERRMEM;
1947 /* WRITE UNLOCK */
1948 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1949 return -1;
1950 }
1951 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
1952 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1953 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
1954 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
1955 break;
1956#endif
1957#ifdef NC_ENABLED_TLS
1958 case NC_TI_OPENSSL:
1959 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1960 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
1961 ERRMEM;
1962 /* WRITE UNLOCK */
1963 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1964 return -1;
1965 }
1966 break;
1967#endif
1968 default:
1969 ERRINT;
1970 /* WRITE UNLOCK */
1971 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1972 return -1;
1973 }
1974
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001975 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
1976
Michal Vasko2e6defd2016-10-07 15:48:15 +02001977 /* set CH default options */
1978 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
1979 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
1980
1981 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
1982
1983 /* WRITE UNLOCK */
1984 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1985
1986 return 0;
1987}
1988
1989API int
Michal Vasko59050372016-11-22 14:33:55 +01001990nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001991{
1992 uint16_t i, j;
1993 int ret = -1;
1994
1995 /* WRITE LOCK */
1996 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1997
Michal Vasko59050372016-11-22 14:33:55 +01001998 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001999 /* remove all CH clients */
2000 for (i = 0; i < server_opts.ch_client_count; ++i) {
2001 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2002
2003 /* remove all endpoints */
2004 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2005 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2006 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2007 }
2008 free(server_opts.ch_clients[i].ch_endpts);
2009
2010 switch (server_opts.ch_clients[i].ti) {
2011#ifdef NC_ENABLED_SSH
2012 case NC_TI_LIBSSH:
2013 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2014 free(server_opts.ch_clients[i].opts.ssh);
2015 break;
2016#endif
2017#ifdef NC_ENABLED_TLS
2018 case NC_TI_OPENSSL:
2019 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2020 free(server_opts.ch_clients[i].opts.tls);
2021 break;
2022#endif
2023 default:
2024 ERRINT;
2025 /* won't get here ...*/
2026 break;
2027 }
2028
2029 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2030
2031 ret = 0;
2032 }
2033 free(server_opts.ch_clients);
2034 server_opts.ch_clients = NULL;
2035
2036 server_opts.ch_client_count = 0;
2037
2038 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002039 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002040 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002041 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 +02002042 /* remove endpt */
2043 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2044
2045 switch (server_opts.ch_clients[i].ti) {
2046#ifdef NC_ENABLED_SSH
2047 case NC_TI_LIBSSH:
2048 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2049 free(server_opts.ch_clients[i].opts.ssh);
2050 break;
2051#endif
2052#ifdef NC_ENABLED_TLS
2053 case NC_TI_OPENSSL:
2054 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2055 free(server_opts.ch_clients[i].opts.tls);
2056 break;
2057#endif
2058 default:
2059 ERRINT;
2060 break;
2061 }
2062
2063 /* remove all endpoints */
2064 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2065 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2066 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2067 }
2068 free(server_opts.ch_clients[i].ch_endpts);
2069
2070 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2071
2072 /* move last client and endpoint(s) to the empty space */
2073 --server_opts.ch_client_count;
2074 if (i < server_opts.ch_client_count) {
2075 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2076 sizeof *server_opts.ch_clients);
2077 } else if (!server_opts.ch_client_count) {
2078 free(server_opts.ch_clients);
2079 server_opts.ch_clients = NULL;
2080 }
2081
2082 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002083 if (name) {
2084 break;
2085 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002086 }
2087 }
2088 }
2089
2090 /* WRITE UNLOCK */
2091 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2092
2093 return ret;
2094}
2095
2096API int
2097nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2098{
2099 uint16_t i;
2100 struct nc_ch_client *client;
2101
2102 if (!client_name) {
2103 ERRARG("client_name");
2104 return -1;
2105 } else if (!endpt_name) {
2106 ERRARG("endpt_name");
2107 return -1;
2108 }
2109
2110 /* LOCK */
2111 client = nc_server_ch_client_lock(client_name, 0, NULL);
2112 if (!client) {
2113 return -1;
2114 }
2115
2116 for (i = 0; i < client->ch_endpt_count; ++i) {
2117 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2118 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2119 /* UNLOCK */
2120 nc_server_ch_client_unlock(client);
2121 return -1;
2122 }
2123 }
2124
2125 ++client->ch_endpt_count;
2126 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2127 if (!client->ch_endpts) {
2128 ERRMEM;
2129 /* UNLOCK */
2130 nc_server_ch_client_unlock(client);
2131 return -1;
2132 }
2133
2134 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2135 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2136 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
2137
2138 /* UNLOCK */
2139 nc_server_ch_client_unlock(client);
2140
2141 return 0;
2142}
2143
2144API int
2145nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2146{
2147 uint16_t i;
2148 int ret = -1;
2149 struct nc_ch_client *client;
2150
2151 if (!client_name) {
2152 ERRARG("client_name");
2153 return -1;
2154 }
2155
2156 /* LOCK */
2157 client = nc_server_ch_client_lock(client_name, 0, NULL);
2158 if (!client) {
2159 return -1;
2160 }
2161
2162 if (!endpt_name) {
2163 /* remove all endpoints */
2164 for (i = 0; i < client->ch_endpt_count; ++i) {
2165 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2166 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2167 }
2168 free(client->ch_endpts);
2169 client->ch_endpts = NULL;
2170 client->ch_endpt_count = 0;
2171
2172 ret = 0;
2173 } else {
2174 for (i = 0; i < client->ch_endpt_count; ++i) {
2175 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2176 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2177 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002178
Michal Vasko4f921012016-10-20 14:07:45 +02002179 /* move last endpoint to the empty space */
2180 --client->ch_endpt_count;
2181 if (i < client->ch_endpt_count) {
2182 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2183 } else if (!server_opts.ch_client_count) {
2184 free(server_opts.ch_clients);
2185 server_opts.ch_clients = NULL;
2186 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002187
Michal Vasko4f921012016-10-20 14:07:45 +02002188 ret = 0;
2189 break;
2190 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002191 }
2192 }
2193
2194 /* UNLOCK */
2195 nc_server_ch_client_unlock(client);
2196
2197 return ret;
2198}
2199
2200API int
2201nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2202{
2203 uint16_t i;
2204 int ret = -1;
2205 struct nc_ch_client *client;
2206
2207 if (!client_name) {
2208 ERRARG("client_name");
2209 return -1;
2210 } else if (!endpt_name) {
2211 ERRARG("endpt_name");
2212 return -1;
2213 } else if (!address) {
2214 ERRARG("address");
2215 return -1;
2216 }
2217
2218 /* LOCK */
2219 client = nc_server_ch_client_lock(client_name, 0, NULL);
2220 if (!client) {
2221 return -1;
2222 }
2223
2224 for (i = 0; i < client->ch_endpt_count; ++i) {
2225 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2226 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2227 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2228
2229 ret = 0;
2230 break;
2231 }
2232 }
2233
2234 /* UNLOCK */
2235 nc_server_ch_client_unlock(client);
2236
2237 if (ret == -1) {
2238 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2239 }
2240
2241 return ret;
2242}
2243
2244API int
2245nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2246{
2247 uint16_t i;
2248 int ret = -1;
2249 struct nc_ch_client *client;
2250
2251 if (!client_name) {
2252 ERRARG("client_name");
2253 return -1;
2254 } else if (!endpt_name) {
2255 ERRARG("endpt_name");
2256 return -1;
2257 } else if (!port) {
2258 ERRARG("port");
2259 return -1;
2260 }
2261
2262 /* LOCK */
2263 client = nc_server_ch_client_lock(client_name, 0, NULL);
2264 if (!client) {
2265 return -1;
2266 }
2267
2268 for (i = 0; i < client->ch_endpt_count; ++i) {
2269 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2270 client->ch_endpts[i].port = port;
2271
2272 ret = 0;
2273 break;
2274 }
2275 }
2276
2277 /* UNLOCK */
2278 nc_server_ch_client_unlock(client);
2279
2280 if (ret == -1) {
2281 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2282 }
2283
2284 return ret;
2285}
2286
2287API int
2288nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2289{
2290 struct nc_ch_client *client;
2291
2292 if (!client_name) {
2293 ERRARG("client_name");
2294 return -1;
2295 } else if (!conn_type) {
2296 ERRARG("conn_type");
2297 return -1;
2298 }
2299
2300 /* LOCK */
2301 client = nc_server_ch_client_lock(client_name, 0, NULL);
2302 if (!client) {
2303 return -1;
2304 }
2305
2306 if (client->conn_type != conn_type) {
2307 client->conn_type = conn_type;
2308
2309 /* set default options */
2310 switch (conn_type) {
2311 case NC_CH_PERSIST:
2312 client->conn.persist.idle_timeout = 86400;
2313 client->conn.persist.ka_max_wait = 30;
2314 client->conn.persist.ka_max_attempts = 3;
2315 break;
2316 case NC_CH_PERIOD:
2317 client->conn.period.idle_timeout = 300;
2318 client->conn.period.reconnect_timeout = 60;
2319 break;
2320 default:
2321 ERRINT;
2322 break;
2323 }
2324 }
2325
2326 /* UNLOCK */
2327 nc_server_ch_client_unlock(client);
2328
2329 return 0;
2330}
2331
2332API int
2333nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2334{
2335 struct nc_ch_client *client;
2336
2337 if (!client_name) {
2338 ERRARG("client_name");
2339 return -1;
2340 }
2341
2342 /* LOCK */
2343 client = nc_server_ch_client_lock(client_name, 0, NULL);
2344 if (!client) {
2345 return -1;
2346 }
2347
2348 if (client->conn_type != NC_CH_PERSIST) {
2349 ERR("Call Home client \"%s\" is not of persistent connection type.");
2350 /* UNLOCK */
2351 nc_server_ch_client_unlock(client);
2352 return -1;
2353 }
2354
2355 client->conn.persist.idle_timeout = idle_timeout;
2356
2357 /* UNLOCK */
2358 nc_server_ch_client_unlock(client);
2359
2360 return 0;
2361}
2362
2363API int
2364nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2365{
2366 struct nc_ch_client *client;
2367
2368 if (!client_name) {
2369 ERRARG("client_name");
2370 return -1;
2371 } else if (!max_wait) {
2372 ERRARG("max_wait");
2373 return -1;
2374 }
2375
2376 /* LOCK */
2377 client = nc_server_ch_client_lock(client_name, 0, NULL);
2378 if (!client) {
2379 return -1;
2380 }
2381
2382 if (client->conn_type != NC_CH_PERSIST) {
2383 ERR("Call Home client \"%s\" is not of persistent connection type.");
2384 /* UNLOCK */
2385 nc_server_ch_client_unlock(client);
2386 return -1;
2387 }
2388
2389 client->conn.persist.ka_max_wait = max_wait;
2390
2391 /* UNLOCK */
2392 nc_server_ch_client_unlock(client);
2393
2394 return 0;
2395}
2396
2397API int
2398nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2399{
2400 struct nc_ch_client *client;
2401
2402 if (!client_name) {
2403 ERRARG("client_name");
2404 return -1;
2405 }
2406
2407 /* LOCK */
2408 client = nc_server_ch_client_lock(client_name, 0, NULL);
2409 if (!client) {
2410 return -1;
2411 }
2412
2413 if (client->conn_type != NC_CH_PERSIST) {
2414 ERR("Call Home client \"%s\" is not of persistent connection type.");
2415 /* UNLOCK */
2416 nc_server_ch_client_unlock(client);
2417 return -1;
2418 }
2419
2420 client->conn.persist.ka_max_attempts = max_attempts;
2421
2422 /* UNLOCK */
2423 nc_server_ch_client_unlock(client);
2424
2425 return 0;
2426}
2427
2428API int
2429nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2430{
2431 struct nc_ch_client *client;
2432
2433 if (!client_name) {
2434 ERRARG("client_name");
2435 return -1;
2436 }
2437
2438 /* LOCK */
2439 client = nc_server_ch_client_lock(client_name, 0, NULL);
2440 if (!client) {
2441 return -1;
2442 }
2443
2444 if (client->conn_type != NC_CH_PERIOD) {
2445 ERR("Call Home client \"%s\" is not of periodic connection type.");
2446 /* UNLOCK */
2447 nc_server_ch_client_unlock(client);
2448 return -1;
2449 }
2450
2451 client->conn.period.idle_timeout = idle_timeout;
2452
2453 /* UNLOCK */
2454 nc_server_ch_client_unlock(client);
2455
2456 return 0;
2457}
2458
2459API int
2460nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2461{
2462 struct nc_ch_client *client;
2463
2464 if (!client_name) {
2465 ERRARG("client_name");
2466 return -1;
2467 } else if (!reconnect_timeout) {
2468 ERRARG("reconnect_timeout");
2469 return -1;
2470 }
2471
2472 /* LOCK */
2473 client = nc_server_ch_client_lock(client_name, 0, NULL);
2474 if (!client) {
2475 return -1;
2476 }
2477
2478 if (client->conn_type != NC_CH_PERIOD) {
2479 ERR("Call Home client \"%s\" is not of periodic connection type.");
2480 /* UNLOCK */
2481 nc_server_ch_client_unlock(client);
2482 return -1;
2483 }
2484
2485 client->conn.period.reconnect_timeout = reconnect_timeout;
2486
2487 /* UNLOCK */
2488 nc_server_ch_client_unlock(client);
2489
2490 return 0;
2491}
2492
2493API int
2494nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2495{
2496 struct nc_ch_client *client;
2497
2498 if (!client_name) {
2499 ERRARG("client_name");
2500 return -1;
2501 }
2502
2503 /* LOCK */
2504 client = nc_server_ch_client_lock(client_name, 0, NULL);
2505 if (!client) {
2506 return -1;
2507 }
2508
2509 client->start_with = start_with;
2510
2511 /* UNLOCK */
2512 nc_server_ch_client_unlock(client);
2513
2514 return 0;
2515}
2516
2517API int
2518nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2519{
2520 struct nc_ch_client *client;
2521
2522 if (!client_name) {
2523 ERRARG("client_name");
2524 return -1;
2525 } else if (!max_attempts) {
2526 ERRARG("max_attempts");
2527 return -1;
2528 }
2529
2530 /* LOCK */
2531 client = nc_server_ch_client_lock(client_name, 0, NULL);
2532 if (!client) {
2533 return -1;
2534 }
2535
2536 client->max_attempts = max_attempts;
2537
2538 /* UNLOCK */
2539 nc_server_ch_client_unlock(client);
2540
2541 return 0;
2542}
2543
2544/* client lock is expected to be held */
2545static NC_MSG_TYPE
2546nc_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 +01002547{
Michal Vasko71090fc2016-05-24 16:37:28 +02002548 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002549 int sock, ret;
2550
Michal Vasko2e6defd2016-10-07 15:48:15 +02002551 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002552 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002553 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002554 }
2555
Michal Vaskoade892d2017-02-22 13:40:35 +01002556 *session = nc_new_session(0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002557 if (!(*session)) {
2558 ERRMEM;
2559 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002560 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002561 }
2562 (*session)->status = NC_STATUS_STARTING;
2563 (*session)->side = NC_SERVER;
2564 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002565 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002566 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2567 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002568
2569 /* transport lock */
Michal Vaskob05053d2016-01-22 16:12:06 +01002570 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01002571 pthread_cond_init((*session)->ti_cond, NULL);
2572 *(*session)->ti_inuse = 0;
Michal Vaskob05053d2016-01-22 16:12:06 +01002573
2574 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002575#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002576 if (client->ti == NC_TI_LIBSSH) {
2577 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002578 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002579 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002580
Michal Vasko71090fc2016-05-24 16:37:28 +02002581 if (ret < 0) {
2582 msgtype = NC_MSG_ERROR;
2583 goto fail;
2584 } else if (!ret) {
2585 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002586 goto fail;
2587 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002588 } else
2589#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002590#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002591 if (client->ti == NC_TI_OPENSSL) {
2592 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002593 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002594 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002595
Michal Vasko71090fc2016-05-24 16:37:28 +02002596 if (ret < 0) {
2597 msgtype = NC_MSG_ERROR;
2598 goto fail;
2599 } else if (!ret) {
2600 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002601 goto fail;
2602 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002603 } else
2604#endif
2605 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002606 ERRINT;
2607 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002608 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002609 goto fail;
2610 }
2611
2612 /* assign new SID atomically */
2613 /* LOCK */
2614 pthread_spin_lock(&server_opts.sid_lock);
2615 (*session)->id = server_opts.new_session_id++;
2616 /* UNLOCK */
2617 pthread_spin_unlock(&server_opts.sid_lock);
2618
2619 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002620 msgtype = nc_handshake(*session);
2621 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002622 goto fail;
2623 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002624 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002625 (*session)->status = NC_STATUS_RUNNING;
2626
Michal Vasko71090fc2016-05-24 16:37:28 +02002627 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002628
2629fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002630 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002631 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002632 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002633}
2634
Michal Vasko2e6defd2016-10-07 15:48:15 +02002635struct nc_ch_client_thread_arg {
2636 char *client_name;
2637 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2638};
2639
2640static struct nc_ch_client *
2641nc_server_ch_client_with_endpt_lock(const char *name)
2642{
2643 struct nc_ch_client *client;
2644
2645 while (1) {
2646 /* LOCK */
2647 client = nc_server_ch_client_lock(name, 0, NULL);
2648 if (!client) {
2649 return NULL;
2650 }
2651 if (client->ch_endpt_count) {
2652 return client;
2653 }
2654 /* no endpoints defined yet */
2655
2656 /* UNLOCK */
2657 nc_server_ch_client_unlock(client);
2658
2659 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2660 }
2661
2662 return NULL;
2663}
2664
2665static int
2666nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2667{
2668 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002669 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002670 struct timespec ts;
2671 struct nc_ch_client *client;
2672
2673 /* session created, initialize condition */
2674 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2675 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2676 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2677 ERRMEM;
2678 nc_session_free(session, NULL);
2679 return -1;
2680 }
2681 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2682 pthread_cond_init(session->opts.server.ch_cond, NULL);
2683
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002684 session->flags |= NC_SESSION_CALLHOME;
2685
Michal Vasko2e6defd2016-10-07 15:48:15 +02002686 /* CH LOCK */
2687 pthread_mutex_lock(session->opts.server.ch_lock);
2688
2689 /* give the session to the user */
2690 data->session_clb(data->client_name, session);
2691
2692 do {
2693 nc_gettimespec(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01002694 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002695
2696 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2697 if (ret && (ret != ETIMEDOUT)) {
2698 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2699 goto ch_client_remove;
2700 }
2701
2702 /* check whether the client was not removed */
2703 /* LOCK */
2704 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2705 if (!client) {
2706 /* client was removed, finish thread */
2707 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2708 data->client_name);
2709 goto ch_client_remove;
2710 }
2711
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002712 if (client->conn_type == NC_CH_PERSIST) {
2713 /* TODO keep-alives */
2714 idle_timeout = client->conn.persist.idle_timeout;
2715 } else {
2716 idle_timeout = client->conn.period.idle_timeout;
2717 }
2718
Michal Vasko3486a7c2017-03-03 13:28:07 +01002719 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 +02002720 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2721 session->status = NC_STATUS_INVALID;
2722 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2723 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002724
2725 /* UNLOCK */
2726 nc_server_ch_client_unlock(client);
2727
2728 } while (session->status == NC_STATUS_RUNNING);
2729
2730 /* CH UNLOCK */
2731 pthread_mutex_unlock(session->opts.server.ch_lock);
2732
2733 return 0;
2734
2735ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002736 /* make the session a standard one */
2737 pthread_cond_destroy(session->opts.server.ch_cond);
2738 free(session->opts.server.ch_cond);
2739 session->opts.server.ch_cond = NULL;
2740
2741 session->flags &= ~NC_SESSION_CALLHOME;
2742
Michal Vasko2e6defd2016-10-07 15:48:15 +02002743 /* CH UNLOCK */
2744 pthread_mutex_unlock(session->opts.server.ch_lock);
2745
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002746 pthread_mutex_destroy(session->opts.server.ch_lock);
2747 free(session->opts.server.ch_lock);
2748 session->opts.server.ch_lock = NULL;
2749
Michal Vasko2e6defd2016-10-07 15:48:15 +02002750 return 1;
2751}
2752
2753static void *
2754nc_ch_client_thread(void *arg)
2755{
2756 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2757 NC_MSG_TYPE msgtype;
2758 uint8_t cur_attempts = 0;
2759 uint16_t i;
Michal Vasko9550cf12017-03-21 15:33:58 +01002760 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002761 struct nc_ch_endpt *cur_endpt;
2762 struct nc_session *session;
2763 struct nc_ch_client *client;
2764
2765 /* LOCK */
2766 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2767 if (!client) {
2768 goto cleanup;
2769 }
2770
2771 cur_endpt = &client->ch_endpts[0];
2772 cur_endpt_name = strdup(cur_endpt->name);
2773
Michal Vasko29af44b2016-10-13 10:59:55 +02002774 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002775 while (1) {
2776 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2777
2778 if (msgtype == NC_MSG_HELLO) {
2779 /* UNLOCK */
2780 nc_server_ch_client_unlock(client);
2781
Michal Vasko29af44b2016-10-13 10:59:55 +02002782 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002783 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2784 goto cleanup;
2785 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002786 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002787
2788 /* LOCK */
2789 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2790 if (!client) {
2791 goto cleanup;
2792 }
2793
2794 /* session changed status -> it was disconnected for whatever reason,
2795 * persistent connection immediately tries to reconnect, periodic waits some first */
2796 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002797 /* UNLOCK */
2798 nc_server_ch_client_unlock(client);
2799
2800 /* TODO wake up sometimes to check for new notifications */
2801 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2802
2803 /* LOCK */
2804 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2805 if (!client) {
2806 goto cleanup;
2807 }
2808 }
2809
2810 /* set next endpoint to try */
2811 if (client->start_with == NC_CH_FIRST_LISTED) {
2812 cur_endpt = &client->ch_endpts[0];
2813 free(cur_endpt_name);
2814 cur_endpt_name = strdup(cur_endpt->name);
2815 } /* else we keep the current one */
2816 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02002817 /* UNLOCK */
2818 nc_server_ch_client_unlock(client);
2819
Michal Vasko2e6defd2016-10-07 15:48:15 +02002820 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002821 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2822
Michal Vasko6bb116b2016-10-26 13:53:46 +02002823 /* LOCK */
2824 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2825 if (!client) {
2826 goto cleanup;
2827 }
2828
Michal Vasko2e6defd2016-10-07 15:48:15 +02002829 ++cur_attempts;
2830 if (cur_attempts == client->max_attempts) {
2831 for (i = 0; i < client->ch_endpt_count; ++i) {
2832 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2833 break;
2834 }
2835 }
2836 if (i < client->ch_endpt_count - 1) {
2837 /* just go to the next endpoint */
2838 cur_endpt = &client->ch_endpts[i + 1];
2839 free(cur_endpt_name);
2840 cur_endpt_name = strdup(cur_endpt->name);
2841 } else {
2842 /* cur_endpoint was removed or is the last, either way start with the first one */
2843 cur_endpt = &client->ch_endpts[0];
2844 free(cur_endpt_name);
2845 cur_endpt_name = strdup(cur_endpt->name);
2846 }
2847
2848 cur_attempts = 0;
2849 } /* else we keep the current one */
2850 }
2851 }
2852
2853cleanup:
2854 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01002855 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002856 free(data->client_name);
2857 free(data);
2858 return NULL;
2859}
2860
2861API int
2862nc_connect_ch_client_dispatch(const char *client_name,
2863 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2864 int ret;
2865 pthread_t tid;
2866 struct nc_ch_client_thread_arg *arg;
2867
2868 if (!client_name) {
2869 ERRARG("client_name");
2870 return -1;
2871 } else if (!session_clb) {
2872 ERRARG("session_clb");
2873 return -1;
2874 }
2875
2876 arg = malloc(sizeof *arg);
2877 if (!arg) {
2878 ERRMEM;
2879 return -1;
2880 }
2881 arg->client_name = strdup(client_name);
2882 if (!arg->client_name) {
2883 ERRMEM;
2884 free(arg);
2885 return -1;
2886 }
2887 arg->session_clb = session_clb;
2888
2889 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2890 if (ret) {
2891 ERR("Creating a new thread failed (%s).", strerror(ret));
2892 free(arg->client_name);
2893 free(arg);
2894 return -1;
2895 }
2896 /* the thread now manages arg */
2897
2898 pthread_detach(tid);
2899
2900 return 0;
2901}
2902
Radek Krejci53691be2016-02-22 13:58:37 +01002903#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002904
Michal Vaskoe8e07702017-03-15 10:19:30 +01002905API int
2906nc_server_endpt_count(void)
2907{
2908 return server_opts.endpt_count;
2909}
2910
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002911API time_t
2912nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02002913{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002914 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02002915 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002916 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02002917 }
2918
Michal Vasko2e6defd2016-10-07 15:48:15 +02002919 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02002920}
Michal Vasko3486a7c2017-03-03 13:28:07 +01002921
2922API void
2923nc_session_set_notif_status(struct nc_session *session, int notif_status)
2924{
2925 if (!session || (session->side != NC_SERVER)) {
2926 ERRARG("session");
2927 return;
2928 }
2929
2930 session->opts.server.ntf_status = (notif_status ? 1 : 0);
2931}
2932
2933API int
2934nc_session_get_notif_status(const struct nc_session *session)
2935{
2936 if (!session || (session->side != NC_SERVER)) {
2937 ERRARG("session");
2938 return 0;
2939 }
2940
2941 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01002942}