blob: 016e5479fa86a7c6f0f44315efdc8fdba3aea37a [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)
77{
78 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);
113
114 /* READ UNLOCK */
115 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;
235 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;
361 } else if (!strcmp(child->schema->name, "format")) {
362 format = ((struct lyd_node_leaf_list *)child)->value_str;
363 }
364 }
365
366 /* check version */
367 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100368 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
369 nc_err_set_msg(err, "The requested version is not supported.", "en");
370 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100371 }
372
373 /* check and get module with the name identifier */
374 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
375 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200376 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
377 }
378 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100379 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
380 nc_err_set_msg(err, "The requested schema was not found.", "en");
381 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100382 }
383
384 /* check format */
Radek Krejci89c34452016-12-07 15:59:45 +0100385 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100386 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
Radek Krejci89c34452016-12-07 15:59:45 +0100387 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100388 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
389 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100390 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
391 nc_err_set_msg(err, "The requested format is not supported.", "en");
392 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100393 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200394 if (!model_data) {
395 ERRINT;
396 return NULL;
397 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100398
Michal Vasko303245c2016-03-24 15:20:03 +0100399 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200400 if (!sdata) {
401 ERRINT;
402 free(model_data);
403 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100404 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200405
Radek Krejci539efb62016-08-24 15:05:16 +0200406 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
407 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100408 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100409 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200410 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100411 return NULL;
412 }
413
Radek Krejci36dfdb32016-09-01 16:56:35 +0200414 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100415}
416
417static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100418nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100419{
Michal Vasko428087d2016-01-14 16:04:28 +0100420 session->term_reason = NC_SESSION_TERM_CLOSED;
421 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100422}
423
Michal Vasko086311b2016-01-08 09:53:11 +0100424API int
425nc_server_init(struct ly_ctx *ctx)
426{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100427 const struct lys_node *rpc;
428
Michal Vasko086311b2016-01-08 09:53:11 +0100429 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200430 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100431 return -1;
432 }
433
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100434 nc_init();
435
Michal Vasko05ba9df2016-01-13 14:40:27 +0100436 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100437 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vaskofd100c92016-03-01 15:23:46 +0100438 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100439 lys_set_private(rpc, nc_clb_default_get_schema);
440 }
441
442 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100443 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100444 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100445 lys_set_private(rpc, nc_clb_default_close_session);
446 }
447
Michal Vasko086311b2016-01-08 09:53:11 +0100448 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100449
450 server_opts.new_session_id = 1;
451 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
452
Michal Vasko086311b2016-01-08 09:53:11 +0100453 return 0;
454}
455
Michal Vaskob48aa812016-01-18 14:13:09 +0100456API void
457nc_server_destroy(void)
458{
Radek Krejci658782b2016-12-04 22:04:55 +0100459 unsigned int i;
460
461 for (i = 0; i < server_opts.capabilities_count; i++) {
462 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
463 }
464 free(server_opts.capabilities);
Michal Vaskob48aa812016-01-18 14:13:09 +0100465 pthread_spin_destroy(&server_opts.sid_lock);
466
Radek Krejci53691be2016-02-22 13:58:37 +0100467#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100468 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100469#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100470#ifdef NC_ENABLED_SSH
471 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100472
473 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
474 server_opts.hostkey_data_free(server_opts.hostkey_data);
475 }
476#endif
477#ifdef NC_ENABLED_TLS
478 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
479 server_opts.server_cert_data_free(server_opts.server_cert_data);
480 }
481 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
482 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
483 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100484#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100485 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100486}
487
Michal Vasko086311b2016-01-08 09:53:11 +0100488API int
489nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
490{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200491 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
492 ERRARG("basic_mode");
493 return -1;
494 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
495 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100496 return -1;
497 }
498
499 server_opts.wd_basic_mode = basic_mode;
500 server_opts.wd_also_supported = also_supported;
501 return 0;
502}
503
Michal Vasko1a38c862016-01-15 15:50:07 +0100504API void
Michal Vasko55f03972016-04-13 08:56:01 +0200505nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
506{
507 if (!basic_mode && !also_supported) {
508 ERRARG("basic_mode and also_supported");
509 return;
510 }
511
512 if (basic_mode) {
513 *basic_mode = server_opts.wd_basic_mode;
514 }
515 if (also_supported) {
516 *also_supported = server_opts.wd_also_supported;
517 }
518}
519
Michal Vasko55f03972016-04-13 08:56:01 +0200520API int
Radek Krejci658782b2016-12-04 22:04:55 +0100521nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200522{
Radek Krejci658782b2016-12-04 22:04:55 +0100523 const char **new;
524
525 if (!value || !value[0]) {
526 ERRARG("value must not be empty");
527 return EXIT_FAILURE;
528 }
529
530 server_opts.capabilities_count++;
531 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
532 if (!new) {
533 ERRMEM;
534 return EXIT_FAILURE;
535 }
536 server_opts.capabilities = new;
537 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
538
539 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200540}
541
Michal Vasko1a38c862016-01-15 15:50:07 +0100542API void
Michal Vasko086311b2016-01-08 09:53:11 +0100543nc_server_set_hello_timeout(uint16_t hello_timeout)
544{
Michal Vasko086311b2016-01-08 09:53:11 +0100545 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100546}
547
Michal Vasko55f03972016-04-13 08:56:01 +0200548API uint16_t
549nc_server_get_hello_timeout(void)
550{
551 return server_opts.hello_timeout;
552}
553
Michal Vasko1a38c862016-01-15 15:50:07 +0100554API void
Michal Vasko086311b2016-01-08 09:53:11 +0100555nc_server_set_idle_timeout(uint16_t idle_timeout)
556{
Michal Vasko086311b2016-01-08 09:53:11 +0100557 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100558}
559
Michal Vasko55f03972016-04-13 08:56:01 +0200560API uint16_t
561nc_server_get_idle_timeout(void)
562{
563 return server_opts.idle_timeout;
564}
565
Michal Vasko71090fc2016-05-24 16:37:28 +0200566API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100567nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100568{
Michal Vasko71090fc2016-05-24 16:37:28 +0200569 NC_MSG_TYPE msgtype;
570
Michal Vasko45e53ae2016-04-07 11:46:03 +0200571 if (!server_opts.ctx) {
572 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200573 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200574 } else if (fdin < 0) {
575 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200576 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200577 } else if (fdout < 0) {
578 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200579 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200580 } else if (!username) {
581 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200582 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200583 } else if (!session) {
584 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200585 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100586 }
587
588 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +0100589 *session = nc_new_session(0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100590 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100591 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200592 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100593 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100594 (*session)->status = NC_STATUS_STARTING;
595 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100596
Michal Vaskoade892d2017-02-22 13:40:35 +0100597 /* transport lock */
598 pthread_mutex_init((*session)->ti_lock, NULL);
599 pthread_cond_init((*session)->ti_cond, NULL);
600 *(*session)->ti_inuse = 0;
601
Michal Vasko086311b2016-01-08 09:53:11 +0100602 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100603 (*session)->ti_type = NC_TI_FD;
604 (*session)->ti.fd.in = fdin;
605 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100606
607 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100608 (*session)->flags = NC_SESSION_SHAREDCTX;
609 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100610
Michal Vaskob48aa812016-01-18 14:13:09 +0100611 /* assign new SID atomically */
612 pthread_spin_lock(&server_opts.sid_lock);
613 (*session)->id = server_opts.new_session_id++;
614 pthread_spin_unlock(&server_opts.sid_lock);
615
Michal Vasko086311b2016-01-08 09:53:11 +0100616 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200617 msgtype = nc_handshake(*session);
618 if (msgtype != NC_MSG_HELLO) {
619 nc_session_free(*session, NULL);
620 *session = NULL;
621 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100622 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200623 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100624 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100625
Michal Vasko71090fc2016-05-24 16:37:28 +0200626 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100627}
Michal Vasko9e036d52016-01-08 10:49:26 +0100628
Michal Vaskob30b99c2016-07-26 11:35:43 +0200629static void
630nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
631{
632 uint8_t i, found = 0;
633
634 for (i = 0; i < ps->queue_len; ++i) {
635 /* idx round buffer adjust */
636 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
637 i = -ps->queue_begin;
638 }
639
640 if (found) {
641 /* move the value back one place */
642 if (ps->queue[ps->queue_begin + i] == id) {
643 /* another equal value, simply cannot be */
644 ERRINT;
645 }
646
647 if (ps->queue_begin + i == 0) {
648 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
649 } else {
650 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
651 }
652 } else if (ps->queue[ps->queue_begin + i] == id) {
653 /* found our id, there can be no more equal valid values */
654 found = 1;
655 }
656 }
657
658 if (!found) {
659 ERRINT;
660 }
661 --ps->queue_len;
662}
663
Michal Vaskof04a52a2016-04-07 10:52:10 +0200664int
Michal Vasko26043172016-07-26 14:08:59 +0200665nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200666{
667 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200668 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200669 struct timespec ts;
670
Radek Krejci7ac16052016-07-15 11:48:18 +0200671 nc_gettimespec(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100672 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200673
674 /* LOCK */
675 ret = pthread_mutex_timedlock(&ps->lock, &ts);
676 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200677 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200678 return -1;
679 }
680
681 /* get a unique queue value (by adding 1 to the last added value, if any) */
682 if (ps->queue_len) {
683 queue_last = ps->queue_begin + ps->queue_len - 1;
684 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
685 queue_last -= NC_PS_QUEUE_SIZE;
686 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200687 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200688 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200689 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200690 }
691
692 /* add ourselves into the queue */
693 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200694 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200695 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200696 return -1;
697 }
698 ++ps->queue_len;
699 queue_last = ps->queue_begin + ps->queue_len - 1;
700 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
701 queue_last -= NC_PS_QUEUE_SIZE;
702 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200703 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200704
705 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200706 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200707 nc_gettimespec(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100708 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200709
710 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
711 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200712 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200713 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200714 nc_ps_queue_remove_id(ps, *id);
715 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200716 return -1;
717 }
718 }
719
Michal Vaskobe86fe32016-04-07 10:43:03 +0200720 /* UNLOCK */
721 pthread_mutex_unlock(&ps->lock);
722
723 return 0;
724}
725
Michal Vaskof04a52a2016-04-07 10:52:10 +0200726int
Michal Vasko26043172016-07-26 14:08:59 +0200727nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200728{
729 int ret;
730 struct timespec ts;
731
Radek Krejci7ac16052016-07-15 11:48:18 +0200732 nc_gettimespec(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100733 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200734
735 /* LOCK */
736 ret = pthread_mutex_timedlock(&ps->lock, &ts);
737 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200738 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200739 ret = -1;
740 }
741
Michal Vaskob30b99c2016-07-26 11:35:43 +0200742 /* we must be the first, it was our turn after all, right? */
743 if (ps->queue[ps->queue_begin] != id) {
744 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200745 /* UNLOCK */
746 if (!ret) {
747 pthread_mutex_unlock(&ps->lock);
748 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200749 return -1;
750 }
751
Michal Vaskobe86fe32016-04-07 10:43:03 +0200752 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200753 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200754
755 /* broadcast to all other threads that the queue moved */
756 pthread_cond_broadcast(&ps->cond);
757
Michal Vaskobe86fe32016-04-07 10:43:03 +0200758 /* UNLOCK */
759 if (!ret) {
760 pthread_mutex_unlock(&ps->lock);
761 }
762
763 return ret;
764}
765
Michal Vasko428087d2016-01-14 16:04:28 +0100766API struct nc_pollsession *
767nc_ps_new(void)
768{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100769 struct nc_pollsession *ps;
770
771 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100772 if (!ps) {
773 ERRMEM;
774 return NULL;
775 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200776 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100777 pthread_mutex_init(&ps->lock, NULL);
778
779 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100780}
781
782API void
783nc_ps_free(struct nc_pollsession *ps)
784{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100785 if (!ps) {
786 return;
787 }
788
Michal Vaskobe86fe32016-04-07 10:43:03 +0200789 if (ps->queue_len) {
790 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
791 }
792
Michal Vasko428087d2016-01-14 16:04:28 +0100793 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100794 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200795 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100796
Michal Vasko428087d2016-01-14 16:04:28 +0100797 free(ps);
798}
799
800API int
801nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
802{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200803 uint8_t q_id;
804
Michal Vasko45e53ae2016-04-07 11:46:03 +0200805 if (!ps) {
806 ERRARG("ps");
807 return -1;
808 } else if (!session) {
809 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100810 return -1;
811 }
812
Michal Vasko48a63ed2016-03-01 09:48:21 +0100813 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200814 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200815 return -1;
816 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100817
Michal Vasko428087d2016-01-14 16:04:28 +0100818 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100819 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko99f251b2017-01-11 11:31:46 +0100820 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100821 ERRMEM;
822 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200823 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100824 return -1;
825 }
Michal Vasko3a715132016-01-21 15:40:31 +0100826 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100827
Michal Vasko48a63ed2016-03-01 09:48:21 +0100828 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200829 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100830}
831
Michal Vasko48a63ed2016-03-01 09:48:21 +0100832static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100833_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100834{
835 uint16_t i;
836
Radek Krejcid5f978f2016-03-03 13:14:45 +0100837 if (index >= 0) {
838 i = (uint16_t)index;
839 goto remove;
840 }
Michal Vasko428087d2016-01-14 16:04:28 +0100841 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100842 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100843remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100844 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100845 if (i < ps->session_count) {
846 ps->sessions[i] = ps->sessions[ps->session_count];
Michal Vasko58005732016-02-02 15:50:52 +0100847 } else if (!ps->session_count) {
848 free(ps->sessions);
849 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +0100850 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +0100851 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100852 return 0;
853 }
854 }
855
Michal Vaskof0537d82016-01-29 14:42:38 +0100856 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100857}
858
Michal Vasko48a63ed2016-03-01 09:48:21 +0100859API int
860nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
861{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200862 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200863 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100864
Michal Vasko45e53ae2016-04-07 11:46:03 +0200865 if (!ps) {
866 ERRARG("ps");
867 return -1;
868 } else if (!session) {
869 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100870 return -1;
871 }
872
873 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200874 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200875 return -1;
876 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100877
Radek Krejcid5f978f2016-03-03 13:14:45 +0100878 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100879
880 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200881 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100882
Michal Vaskobe86fe32016-04-07 10:43:03 +0200883 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100884}
885
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100886API struct nc_session *
887nc_ps_get_session_by_sid(const struct nc_pollsession *ps, uint32_t sid)
888{
889 uint8_t q_id;
890 uint16_t i;
891 struct nc_session *ret = NULL;
892
893 if (!ps) {
894 ERRARG("ps");
895 return NULL;
896 }
897
898 /* LOCK */
899 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
900 return NULL;
901 }
902
903 for (i = 0; i < ps->session_count; ++i) {
904 if (ps->sessions[i]->id == sid) {
905 ret = ps->sessions[i];
906 break;
907 }
908 }
909
910 /* UNLOCK */
911 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
912
913 return ret;
914}
915
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100916API uint16_t
917nc_ps_session_count(struct nc_pollsession *ps)
918{
919 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200920 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100921 return 0;
922 }
923
Michal Vaskof4462fd2017-02-15 14:29:05 +0100924 return ps->session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100925}
926
Michal Vasko71090fc2016-05-24 16:37:28 +0200927/* must be called holding the session lock!
928 * returns: NC_PSPOLL_ERROR,
929 * NC_PSPOLL_BAD_RPC,
930 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
931 * NC_PSPOLL_RPC
932 */
933static int
Radek Krejci93e80222016-10-03 13:34:25 +0200934nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100935{
936 struct lyxml_elem *xml = NULL;
937 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200938 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200939 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100940
Michal Vasko45e53ae2016-04-07 11:46:03 +0200941 if (!session) {
942 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200943 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200944 } else if (!rpc) {
945 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200946 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100947 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100948 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200949 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100950 }
951
952 msgtype = nc_read_msg(session, &xml);
953
954 switch (msgtype) {
955 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200956 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100957 if (!*rpc) {
958 ERRMEM;
959 goto error;
960 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100961
Radek Krejcif93c7d42016-04-06 13:41:15 +0200962 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +0100963 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
964 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100965 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200966 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200967 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200968 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200969 nc_server_reply_free(reply);
970 if (ret == -1) {
971 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200972 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200973 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
974 } else {
975 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100976 }
Michal Vasko428087d2016-01-14 16:04:28 +0100977 (*rpc)->root = xml;
978 break;
979 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100980 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200981 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100982 goto error;
983 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100984 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200985 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100986 goto error;
987 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100988 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200989 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100990 goto error;
991 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200992 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100993 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
994 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200995 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100996 break;
997 }
998
Michal Vasko71090fc2016-05-24 16:37:28 +0200999 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001000
1001error:
1002 /* cleanup */
1003 lyxml_free(server_opts.ctx, xml);
1004
Michal Vasko71090fc2016-05-24 16:37:28 +02001005 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001006}
1007
fanchanghu966f2de2016-07-21 02:28:57 -04001008API void
1009nc_set_global_rpc_clb(nc_rpc_clb clb)
1010{
1011 global_rpc_clb = clb;
1012}
1013
Radek Krejci93e80222016-10-03 13:34:25 +02001014API NC_MSG_TYPE
1015nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1016{
1017 NC_MSG_TYPE result = NC_MSG_NOTIF;
1018 int ret;
1019
1020 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001021 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001022 ERRARG("session");
1023 return NC_MSG_ERROR;
1024 } else if (!notif || !notif->tree || !notif->eventtime) {
1025 ERRARG("notif");
1026 return NC_MSG_ERROR;
1027 }
1028
1029 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vaskoade892d2017-02-22 13:40:35 +01001030 ret = nc_session_lock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001031 if (ret < 0) {
1032 return NC_MSG_ERROR;
1033 } else if (!ret) {
1034 return NC_MSG_WOULDBLOCK;
1035 }
1036
1037 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1038 if (ret == -1) {
1039 ERR("Session %u: failed to write notification.", session->id);
1040 result = NC_MSG_ERROR;
1041 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001042
1043 nc_session_unlock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001044
1045 return result;
1046}
1047
Michal Vasko71090fc2016-05-24 16:37:28 +02001048/* must be called holding the session lock!
1049 * returns: NC_PSPOLL_ERROR,
1050 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1051 * NC_PSPOLL_REPLY_ERROR,
1052 * 0
1053 */
1054static int
Radek Krejci93e80222016-10-03 13:34:25 +02001055nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001056{
1057 nc_rpc_clb clb;
1058 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001059 struct lys_node *rpc_act = NULL;
1060 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001061 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001062
Michal Vasko4a827e52016-03-03 10:59:00 +01001063 if (!rpc) {
1064 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001065 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001066 }
1067
Michal Vasko90e8e692016-07-13 12:27:57 +02001068 if (rpc->tree->schema->nodetype == LYS_RPC) {
1069 /* RPC */
1070 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001071 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001072 /* action */
1073 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1074 if (elem->schema->nodetype == LYS_ACTION) {
1075 rpc_act = elem->schema;
1076 break;
1077 }
1078 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001079 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001080 if (!rpc_act) {
1081 ERRINT;
1082 return NC_PSPOLL_ERROR;
1083 }
1084 }
1085
1086 if (!rpc_act->priv) {
1087 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001088 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001089 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001090 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001091 reply = clb(rpc->tree, session);
1092 }
1093
1094 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001095 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001096 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001097 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1098 if (reply->type == NC_RPL_ERROR) {
1099 ret |= NC_PSPOLL_REPLY_ERROR;
1100 }
1101 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001102
Michal Vasko71090fc2016-05-24 16:37:28 +02001103 if (r == -1) {
1104 ERR("Session %u: failed to write reply.", session->id);
1105 ret |= NC_PSPOLL_ERROR;
1106 }
Michal Vasko428087d2016-01-14 16:04:28 +01001107
1108 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1109 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1110 session->status = NC_STATUS_INVALID;
1111 }
1112
Michal Vasko71090fc2016-05-24 16:37:28 +02001113 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001114}
1115
1116API int
Michal Vasko71090fc2016-05-24 16:37:28 +02001117nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +01001118{
Michal Vasko99f251b2017-01-11 11:31:46 +01001119 int ret, r, poll_ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001120 uint8_t q_id;
Michal Vasko99f251b2017-01-11 11:31:46 +01001121 uint16_t i, j;
1122 char msg[256];
1123 NC_SESSION_TERM_REASON term_reason;
1124 struct pollfd pfd;
Michal Vasko36c7be82017-02-22 13:37:59 +01001125 struct timespec ts_timeout, ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +02001126 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +01001127 struct nc_server_rpc *rpc = NULL;
Michal Vasko99f251b2017-01-11 11:31:46 +01001128#ifdef NC_ENABLED_SSH
1129 struct nc_session *new;
1130#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001131
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001132 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001133 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001134 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001135 }
1136
Michal Vaskoade892d2017-02-22 13:40:35 +01001137 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001138 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001139 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001140 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001141
Michal Vaskoade892d2017-02-22 13:40:35 +01001142 if (!ps->session_count) {
1143 ret = NC_PSPOLL_NOSESSIONS;
1144 goto ps_unlock_finish;
1145 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001146
Michal Vasko36c7be82017-02-22 13:37:59 +01001147 /* check timeout of all the sessions */
1148 nc_gettimespec(&ts_cur);
1149 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3486a7c2017-03-03 13:28:07 +01001150 if (!(ps->sessions[i]->flags & NC_SESSION_CALLHOME) && !ps->sessions[i]->opts.server.ntf_status
1151 && server_opts.idle_timeout
Michal Vasko36c7be82017-02-22 13:37:59 +01001152 && (ts_cur.tv_sec >= ps->sessions[i]->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001153 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1154 ps->sessions[i]->status = NC_STATUS_INVALID;
1155 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001156 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1157 if (session) {
1158 *session = ps->sessions[i];
1159 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001160 goto ps_unlock_finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001161 }
Michal Vasko428087d2016-01-14 16:04:28 +01001162 }
1163
Michal Vasko36c7be82017-02-22 13:37:59 +01001164 if (timeout > -1) {
1165 nc_gettimespec(&ts_timeout);
1166 nc_addtimespec(&ts_timeout, timeout);
1167 }
1168
1169 /* poll all the sessions one-by-one */
Michal Vasko99f251b2017-01-11 11:31:46 +01001170 do {
1171 /* loop from i to j */
1172 if (ps->last_event_session == ps->session_count - 1) {
1173 i = j = 0;
1174 } else {
1175 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001176 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001177 do {
1178 cur_session = ps->sessions[i];
Michal Vasko428087d2016-01-14 16:04:28 +01001179
Michal Vaskoade892d2017-02-22 13:40:35 +01001180 /* SESSION LOCK */
1181 if ((cur_session->status == NC_STATUS_RUNNING)
1182 && !*cur_session->ti_inuse && ((r = nc_session_lock(cur_session, 0, __func__)))) {
1183 /* we go here if we successfully lock the session or there was an error, on timeout we simply skip it */
1184 if (r == -1) {
1185 ret = NC_PSPOLL_ERROR;
1186 goto ps_unlock_finish;
1187 }
1188 /* damn race condition */
1189 if (cur_session->status != NC_STATUS_RUNNING) {
1190 /* SESSION UNLOCK */
1191 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
1192 goto next_iteration;
1193 }
1194
1195 switch (cur_session->ti_type) {
Radek Krejci53691be2016-02-22 13:58:37 +01001196#ifdef NC_ENABLED_SSH
Michal Vaskoade892d2017-02-22 13:40:35 +01001197 case NC_TI_LIBSSH:
1198 r = ssh_channel_poll_timeout(cur_session->ti.libssh.channel, 0, 0);
1199 if (r < 1) {
1200 if (r == SSH_EOF) {
1201 sprintf(msg, "SSH channel unexpected EOF");
1202 term_reason = NC_SESSION_TERM_DROPPED;
1203 poll_ret = -2;
1204 } else if (r == SSH_ERROR) {
1205 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(cur_session->ti.libssh.session));
1206 term_reason = NC_SESSION_TERM_OTHER;
1207 poll_ret = -2;
1208 } else {
1209 poll_ret = 0;
1210 }
1211 break;
1212 }
1213
1214 /* we have some data, but it may be just an SSH message */
1215 r = ssh_execute_message_callbacks(cur_session->ti.libssh.session);
1216 if (r != SSH_OK) {
1217 sprintf(msg, "failed to receive SSH messages (%s)", ssh_get_error(cur_session->ti.libssh.session));
Michal Vasko76be6752017-01-19 12:14:48 +01001218 term_reason = NC_SESSION_TERM_OTHER;
1219 poll_ret = -2;
Michal Vaskoade892d2017-02-22 13:40:35 +01001220 } else if (cur_session->flags & NC_SESSION_SSH_NEW_MSG) {
1221 /* new SSH message */
1222 cur_session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1223 if (cur_session->ti.libssh.next) {
1224 for (new = cur_session->ti.libssh.next; new != cur_session; new = new->ti.libssh.next) {
1225 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1226 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1227 /* new NETCONF SSH channel */
1228 if (session) {
1229 *session = cur_session;
1230 }
1231 ret = NC_PSPOLL_SSH_CHANNEL;
1232 goto session_ps_unlock_finish;
Michal Vasko99f251b2017-01-11 11:31:46 +01001233 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001234 }
1235 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001236
Michal Vaskoade892d2017-02-22 13:40:35 +01001237 /* just some SSH message */
1238 if (session) {
1239 *session = cur_session;
1240 }
1241 ret = NC_PSPOLL_SSH_MSG;
1242 goto session_ps_unlock_finish;
1243 } else {
1244 /* we have some application data */
1245 poll_ret = 1;
Michal Vasko99f251b2017-01-11 11:31:46 +01001246 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001247 break;
Michal Vasko99f251b2017-01-11 11:31:46 +01001248#endif
1249#ifdef NC_ENABLED_TLS
Michal Vaskoade892d2017-02-22 13:40:35 +01001250 case NC_TI_OPENSSL:
1251 r = SSL_pending(cur_session->ti.tls);
1252 if (!r) {
1253 /* no data pending in the SSL buffer, poll fd */
1254 pfd.fd = SSL_get_rfd(cur_session->ti.tls);
1255 if (pfd.fd < 0) {
1256 ERRINT;
1257 ret = NC_PSPOLL_ERROR;
1258 goto session_ps_unlock_finish;
1259 }
1260 pfd.events = POLLIN;
1261 pfd.revents = 0;
1262 r = poll(&pfd, 1, 0);
1263
1264 if ((r < 0) && (errno != EINTR)) {
1265 sprintf(msg, "poll failed (%s)", strerror(errno));
1266 poll_ret = -1;
1267 } else if (r > 0) {
1268 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1269 sprintf(msg, "communication socket unexpectedly closed");
1270 term_reason = NC_SESSION_TERM_DROPPED;
1271 poll_ret = -2;
1272 } else if (pfd.revents & POLLERR) {
1273 sprintf(msg, "communication socket error");
1274 term_reason = NC_SESSION_TERM_OTHER;
1275 poll_ret = -2;
1276 } else {
1277 poll_ret = 1;
1278 }
1279 } else {
1280 poll_ret = 0;
1281 }
1282 } else {
1283 poll_ret = 1;
Michal Vasko99f251b2017-01-11 11:31:46 +01001284 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001285 break;
1286#endif
1287 case NC_TI_FD:
1288 pfd.fd = cur_session->ti.fd.in;
Michal Vasko99f251b2017-01-11 11:31:46 +01001289 pfd.events = POLLIN;
1290 pfd.revents = 0;
1291 r = poll(&pfd, 1, 0);
1292
Michal Vaskoade892d2017-02-22 13:40:35 +01001293 if ((r < 0) && (errno != EINTR)) {
Michal Vasko99f251b2017-01-11 11:31:46 +01001294 sprintf(msg, "poll failed (%s)", strerror(errno));
1295 poll_ret = -1;
1296 } else if (r > 0) {
1297 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1298 sprintf(msg, "communication socket unexpectedly closed");
1299 term_reason = NC_SESSION_TERM_DROPPED;
1300 poll_ret = -2;
1301 } else if (pfd.revents & POLLERR) {
1302 sprintf(msg, "communication socket error");
1303 term_reason = NC_SESSION_TERM_OTHER;
1304 poll_ret = -2;
1305 } else {
1306 poll_ret = 1;
1307 }
1308 } else {
1309 poll_ret = 0;
1310 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001311 break;
1312 case NC_TI_NONE:
1313 ERRINT;
1314 ret = NC_PSPOLL_ERROR;
1315 goto session_ps_unlock_finish;
Michal Vasko99f251b2017-01-11 11:31:46 +01001316 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001317
Michal Vaskoade892d2017-02-22 13:40:35 +01001318 /* here: poll_ret == -2 - session error, session terminated,
1319 * poll_ret == -1 - generic error,
1320 * poll_ret == 0 - nothing to read,
1321 * poll_ret > 0 - data available
1322 */
1323 if (poll_ret == -2) {
1324 ERR("Session %u: %s.", cur_session->id, msg);
1325 cur_session->status = NC_STATUS_INVALID;
1326 cur_session->term_reason = term_reason;
1327 if (session) {
1328 *session = cur_session;
Michal Vasko99f251b2017-01-11 11:31:46 +01001329 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001330 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1331 goto session_ps_unlock_finish;
1332 } else if (poll_ret == -1) {
1333 ERR("Session %u: %s.", cur_session->id, msg);
1334 ret = NC_PSPOLL_ERROR;
1335 goto session_ps_unlock_finish;
1336 } else if (poll_ret > 0) {
1337 break;
Michal Vasko99f251b2017-01-11 11:31:46 +01001338 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001339
1340 /* SESSION UNLOCK */
1341 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
1342 } else {
1343 /* timeout */
1344 poll_ret = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001345 }
Michal Vasko428087d2016-01-14 16:04:28 +01001346
Michal Vaskoade892d2017-02-22 13:40:35 +01001347next_iteration:
Michal Vasko99f251b2017-01-11 11:31:46 +01001348 if (i == ps->session_count - 1) {
1349 i = 0;
1350 } else {
1351 ++i;
1352 }
1353 } while (i != j);
1354
Michal Vaskoade892d2017-02-22 13:40:35 +01001355 /* no event, no session remains locked */
Michal Vasko99f251b2017-01-11 11:31:46 +01001356 if (!poll_ret && (timeout > -1)) {
1357 usleep(NC_TIMEOUT_STEP);
1358
Michal Vaskoade892d2017-02-22 13:40:35 +01001359 nc_gettimespec(&ts_cur);
Michal Vasko99f251b2017-01-11 11:31:46 +01001360 /* final timeout */
Michal Vaskoade892d2017-02-22 13:40:35 +01001361 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
Michal Vasko99f251b2017-01-11 11:31:46 +01001362 ret = NC_PSPOLL_TIMEOUT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001363 goto ps_unlock_finish;
Michal Vasko99f251b2017-01-11 11:31:46 +01001364 }
Michal Vasko428087d2016-01-14 16:04:28 +01001365 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001366 } while (!poll_ret);
Michal Vasko428087d2016-01-14 16:04:28 +01001367
Michal Vaskoade892d2017-02-22 13:40:35 +01001368 /* this is the session with some data available for reading, it is still locked */
Michal Vasko71090fc2016-05-24 16:37:28 +02001369 if (session) {
1370 *session = cur_session;
1371 }
Michal Vasko99f251b2017-01-11 11:31:46 +01001372 ps->last_event_session = i;
Michal Vasko428087d2016-01-14 16:04:28 +01001373
Michal Vaskoade892d2017-02-22 13:40:35 +01001374 /* PS UNLOCK */
1375 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001376
Radek Krejci93e80222016-10-03 13:34:25 +02001377 ret = nc_server_recv_rpc(cur_session, &rpc);
Michal Vasko71090fc2016-05-24 16:37:28 +02001378 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001379 if (cur_session->status != NC_STATUS_RUNNING) {
1380 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001381 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001382 goto session_unlock_finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001383 }
1384
Michal Vasko2e6defd2016-10-07 15:48:15 +02001385 cur_session->opts.server.last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001386
Michal Vaskoade892d2017-02-22 13:40:35 +01001387 /* process RPC, not needed afterwards */
Radek Krejci93e80222016-10-03 13:34:25 +02001388 ret |= nc_server_send_reply(cur_session, rpc);
Michal Vaskoade892d2017-02-22 13:40:35 +01001389 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vasko428087d2016-01-14 16:04:28 +01001390
Michal Vasko71090fc2016-05-24 16:37:28 +02001391 if (cur_session->status != NC_STATUS_RUNNING) {
1392 ret |= NC_PSPOLL_SESSION_TERM;
1393 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1394 ret |= NC_PSPOLL_SESSION_ERROR;
1395 }
Michal Vasko428087d2016-01-14 16:04:28 +01001396 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001397
Michal Vaskoade892d2017-02-22 13:40:35 +01001398session_unlock_finish:
1399 /* SESSION UNLOCK */
1400 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
1401 return ret;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001402
Michal Vaskoade892d2017-02-22 13:40:35 +01001403session_ps_unlock_finish:
1404 /* SESSION UNLOCK */
1405 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
1406
1407ps_unlock_finish:
1408 /* PS UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001409 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001410 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001411}
1412
Michal Vaskod09eae62016-02-01 10:32:52 +01001413API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001414nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001415{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001416 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001417 uint16_t i;
1418 struct nc_session *session;
1419
Michal Vasko9a25e932016-02-01 10:36:42 +01001420 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001421 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001422 return;
1423 }
1424
Michal Vasko48a63ed2016-03-01 09:48:21 +01001425 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001426 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001427 return;
1428 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001429
Michal Vasko48a63ed2016-03-01 09:48:21 +01001430 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001431 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001432 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001433 }
1434 free(ps->sessions);
1435 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001436 ps->session_count = 0;
Michal Vasko99f251b2017-01-11 11:31:46 +01001437 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001438 } else {
1439 for (i = 0; i < ps->session_count; ) {
1440 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1441 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001442 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001443 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001444 continue;
1445 }
1446
1447 ++i;
1448 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001449 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001450
1451 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001452 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001453}
1454
Radek Krejci53691be2016-02-22 13:58:37 +01001455#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001456
Michal Vaskoe2713da2016-08-22 16:06:40 +02001457API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001458nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001459{
Michal Vasko3031aae2016-01-27 16:07:18 +01001460 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001461 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001462
Michal Vasko45e53ae2016-04-07 11:46:03 +02001463 if (!name) {
1464 ERRARG("name");
1465 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001466 }
1467
Michal Vaskoade892d2017-02-22 13:40:35 +01001468 /* BIND LOCK */
1469 pthread_mutex_lock(&server_opts.bind_lock);
1470
1471 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001472 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001473
1474 /* check name uniqueness */
1475 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001476 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001477 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001478 ret = -1;
1479 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001480 }
1481 }
1482
Michal Vasko3031aae2016-01-27 16:07:18 +01001483 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001484 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001485 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001486 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001487 ret = -1;
1488 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001489 }
1490 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001491 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001492
Michal Vaskoe2713da2016-08-22 16:06:40 +02001493 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001494 if (!server_opts.binds) {
1495 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001496 ret = -1;
1497 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001498 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001499
Michal Vasko2e6defd2016-10-07 15:48:15 +02001500 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1501 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1502 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001503 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001504
1505 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001506#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001507 case NC_TI_LIBSSH:
1508 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1509 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1510 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001511 ret = -1;
1512 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001513 }
1514 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1515 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1516 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1517 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1518 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001519#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001520#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001521 case NC_TI_OPENSSL:
1522 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1523 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1524 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001525 ret = -1;
1526 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001527 }
1528 break;
1529#endif
1530 default:
1531 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001532 ret = -1;
1533 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001534 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001535
Michal Vaskoade892d2017-02-22 13:40:35 +01001536cleanup:
1537 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001538 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001539
Michal Vaskoade892d2017-02-22 13:40:35 +01001540 /* BIND UNLOCK */
1541 pthread_mutex_unlock(&server_opts.bind_lock);
1542
1543 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001544}
1545
Michal Vasko3031aae2016-01-27 16:07:18 +01001546int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001547nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001548{
1549 struct nc_endpt *endpt;
1550 struct nc_bind *bind = NULL;
1551 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001552 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001553
Michal Vasko45e53ae2016-04-07 11:46:03 +02001554 if (!endpt_name) {
1555 ERRARG("endpt_name");
1556 return -1;
1557 } else if ((!address && !port) || (address && port)) {
1558 ERRARG("address and port");
1559 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001560 }
1561
Michal Vaskoe2713da2016-08-22 16:06:40 +02001562 if (address) {
1563 set_addr = 1;
1564 } else {
1565 set_addr = 0;
1566 }
1567
Michal Vaskoade892d2017-02-22 13:40:35 +01001568 /* BIND LOCK */
1569 pthread_mutex_lock(&server_opts.bind_lock);
1570
1571 /* ENDPT LOCK */
1572 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001573 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001574 /* BIND UNLOCK */
1575 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001576 return -1;
1577 }
1578
Michal Vaskoe2713da2016-08-22 16:06:40 +02001579 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001580
Michal Vaskoe2713da2016-08-22 16:06:40 +02001581 if (set_addr) {
1582 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001583 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001584 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001585 }
1586
Michal Vaskoe2713da2016-08-22 16:06:40 +02001587 /* we have all the information we need to create a listening socket */
1588 if (address && port) {
1589 /* create new socket, close the old one */
1590 sock = nc_sock_listen(address, port);
1591 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001592 ret = -1;
1593 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001594 }
1595
1596 if (bind->sock > -1) {
1597 close(bind->sock);
1598 }
1599 bind->sock = sock;
1600 } /* else we are just setting address or port */
1601
1602 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001603 lydict_remove(server_opts.ctx, bind->address);
1604 bind->address = lydict_insert(server_opts.ctx, address, 0);
1605 } else {
1606 bind->port = port;
1607 }
1608
Michal Vaskoe2713da2016-08-22 16:06:40 +02001609 if (sock > -1) {
1610#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001611 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001612#elif defined(NC_ENABLED_SSH)
1613 VRB("Listening on %s:%u for SSH connections.", address, port);
1614#else
1615 VRB("Listening on %s:%u for TLS connections.", address, port);
1616#endif
1617 }
1618
Michal Vasko4c1fb492017-01-30 14:31:07 +01001619cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001620 /* ENDPT UNLOCK */
1621 pthread_rwlock_unlock(&server_opts.endpt_lock);
1622
1623 /* BIND UNLOCK */
1624 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001625
Michal Vasko4c1fb492017-01-30 14:31:07 +01001626 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001627}
1628
Michal Vaskoe2713da2016-08-22 16:06:40 +02001629API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001630nc_server_endpt_set_address(const char *endpt_name, const char *address)
1631{
1632 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1633}
1634
1635API int
1636nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1637{
1638 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1639}
1640
1641API int
Michal Vasko59050372016-11-22 14:33:55 +01001642nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001643{
1644 uint32_t i;
1645 int ret = -1;
1646
Michal Vaskoade892d2017-02-22 13:40:35 +01001647 /* BIND LOCK */
1648 pthread_mutex_lock(&server_opts.bind_lock);
1649
1650 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001651 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001652
Michal Vasko59050372016-11-22 14:33:55 +01001653 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001654 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001655 for (i = 0; i < server_opts.endpt_count; ++i) {
1656 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001657 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001658#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001659 case NC_TI_LIBSSH:
1660 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1661 free(server_opts.endpts[i].opts.ssh);
1662 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001663#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001664#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001665 case NC_TI_OPENSSL:
1666 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1667 free(server_opts.endpts[i].opts.tls);
1668 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001669#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001670 default:
1671 ERRINT;
1672 /* won't get here ...*/
1673 break;
1674 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001675 ret = 0;
1676 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001677 free(server_opts.endpts);
1678 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001679
1680 /* remove all binds */
1681 for (i = 0; i < server_opts.endpt_count; ++i) {
1682 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1683 if (server_opts.binds[i].sock > -1) {
1684 close(server_opts.binds[i].sock);
1685 }
1686 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001687 free(server_opts.binds);
1688 server_opts.binds = NULL;
1689
Michal Vasko3031aae2016-01-27 16:07:18 +01001690 server_opts.endpt_count = 0;
1691
Michal Vasko1a38c862016-01-15 15:50:07 +01001692 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001693 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001694 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001695 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001696 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001697 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001698 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001699#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001700 case NC_TI_LIBSSH:
1701 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1702 free(server_opts.endpts[i].opts.ssh);
1703 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001704#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001705#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001706 case NC_TI_OPENSSL:
1707 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1708 free(server_opts.endpts[i].opts.tls);
1709 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001710#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001711 default:
1712 ERRINT;
1713 break;
1714 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001715
Michal Vaskoe2713da2016-08-22 16:06:40 +02001716 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001717 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1718 if (server_opts.binds[i].sock > -1) {
1719 close(server_opts.binds[i].sock);
1720 }
1721
1722 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001723 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001724 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001725 free(server_opts.binds);
1726 server_opts.binds = NULL;
1727 free(server_opts.endpts);
1728 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001729 } else if (i < server_opts.endpt_count) {
1730 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1731 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001732 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001733
1734 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001735 if (name) {
1736 break;
1737 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001738 }
1739 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001740 }
1741
Michal Vaskoade892d2017-02-22 13:40:35 +01001742 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001743 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001744
Michal Vaskoade892d2017-02-22 13:40:35 +01001745 /* BIND UNLOCK */
1746 pthread_mutex_unlock(&server_opts.bind_lock);
1747
Michal Vasko9e036d52016-01-08 10:49:26 +01001748 return ret;
1749}
1750
Michal Vasko71090fc2016-05-24 16:37:28 +02001751API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001752nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001753{
Michal Vasko71090fc2016-05-24 16:37:28 +02001754 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001755 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001756 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001757 uint16_t port, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001758
Michal Vasko45e53ae2016-04-07 11:46:03 +02001759 if (!server_opts.ctx) {
1760 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001761 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001762 } else if (!session) {
1763 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001764 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001765 }
1766
Michal Vaskoade892d2017-02-22 13:40:35 +01001767 /* BIND LOCK */
1768 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001769
1770 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001771 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01001772 /* BIND UNLOCK */
1773 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001774 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001775 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001776
Michal Vaskoe2713da2016-08-22 16:06:40 +02001777 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001778 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001779 /* BIND UNLOCK */
1780 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001781 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001782 if (!ret) {
1783 return NC_MSG_WOULDBLOCK;
1784 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001785 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001786 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001787
1788 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
1789 /* ENDPT READ LOCK */
1790 pthread_rwlock_rdlock(&server_opts.endpt_lock);
1791
1792 /* BIND UNLOCK */
1793 pthread_mutex_unlock(&server_opts.bind_lock);
1794
Michal Vaskob48aa812016-01-18 14:13:09 +01001795 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001796
Michal Vaskoade892d2017-02-22 13:40:35 +01001797 *session = nc_new_session(0);
Michal Vasko686aa312016-01-21 15:58:18 +01001798 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001799 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001800 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001801 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001802 msgtype = NC_MSG_ERROR;
1803 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001804 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001805 (*session)->status = NC_STATUS_STARTING;
1806 (*session)->side = NC_SERVER;
1807 (*session)->ctx = server_opts.ctx;
1808 (*session)->flags = NC_SESSION_SHAREDCTX;
1809 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1810 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001811
1812 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001813 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001814 pthread_cond_init((*session)->ti_cond, NULL);
1815 *(*session)->ti_inuse = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001816
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001817 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001818#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001819 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1820 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001821 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001822 if (ret < 0) {
1823 msgtype = NC_MSG_ERROR;
1824 goto cleanup;
1825 } else if (!ret) {
1826 msgtype = NC_MSG_WOULDBLOCK;
1827 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001828 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001829 } else
1830#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001831#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001832 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1833 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001834 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001835 if (ret < 0) {
1836 msgtype = NC_MSG_ERROR;
1837 goto cleanup;
1838 } else if (!ret) {
1839 msgtype = NC_MSG_WOULDBLOCK;
1840 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001841 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001842 } else
1843#endif
1844 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001845 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001846 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001847 msgtype = NC_MSG_ERROR;
1848 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001849 }
1850
Michal Vasko2cc4c682016-03-01 09:16:48 +01001851 (*session)->data = NULL;
1852
Michal Vaskoade892d2017-02-22 13:40:35 +01001853 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001854 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001855
Michal Vaskob48aa812016-01-18 14:13:09 +01001856 /* assign new SID atomically */
1857 /* LOCK */
1858 pthread_spin_lock(&server_opts.sid_lock);
1859 (*session)->id = server_opts.new_session_id++;
1860 /* UNLOCK */
1861 pthread_spin_unlock(&server_opts.sid_lock);
1862
Michal Vasko9e036d52016-01-08 10:49:26 +01001863 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001864 msgtype = nc_handshake(*session);
1865 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001866 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001867 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001868 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001869 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001870 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001871 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001872
Michal Vasko71090fc2016-05-24 16:37:28 +02001873 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001874
Michal Vasko71090fc2016-05-24 16:37:28 +02001875cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001876 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001877 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001878
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001879 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001880 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001881 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001882}
1883
Michal Vasko2e6defd2016-10-07 15:48:15 +02001884API int
1885nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1886{
1887 uint16_t i;
1888
1889 if (!name) {
1890 ERRARG("name");
1891 return -1;
1892 } else if (!ti) {
1893 ERRARG("ti");
1894 return -1;
1895 }
1896
1897 /* WRITE LOCK */
1898 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1899
1900 /* check name uniqueness */
1901 for (i = 0; i < server_opts.ch_client_count; ++i) {
1902 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1903 ERR("Call Home client \"%s\" already exists.", name);
1904 /* WRITE UNLOCK */
1905 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1906 return -1;
1907 }
1908 }
1909
1910 ++server_opts.ch_client_count;
1911 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
1912 if (!server_opts.ch_clients) {
1913 ERRMEM;
1914 /* WRITE UNLOCK */
1915 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1916 return -1;
1917 }
1918 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1919 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001920 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
1921 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001922
1923 switch (ti) {
1924#ifdef NC_ENABLED_SSH
1925 case NC_TI_LIBSSH:
1926 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1927 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
1928 ERRMEM;
1929 /* WRITE UNLOCK */
1930 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1931 return -1;
1932 }
1933 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
1934 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1935 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
1936 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
1937 break;
1938#endif
1939#ifdef NC_ENABLED_TLS
1940 case NC_TI_OPENSSL:
1941 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1942 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
1943 ERRMEM;
1944 /* WRITE UNLOCK */
1945 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1946 return -1;
1947 }
1948 break;
1949#endif
1950 default:
1951 ERRINT;
1952 /* WRITE UNLOCK */
1953 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1954 return -1;
1955 }
1956
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001957 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
1958
Michal Vasko2e6defd2016-10-07 15:48:15 +02001959 /* set CH default options */
1960 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
1961 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
1962
1963 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
1964
1965 /* WRITE UNLOCK */
1966 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1967
1968 return 0;
1969}
1970
1971API int
Michal Vasko59050372016-11-22 14:33:55 +01001972nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001973{
1974 uint16_t i, j;
1975 int ret = -1;
1976
1977 /* WRITE LOCK */
1978 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1979
Michal Vasko59050372016-11-22 14:33:55 +01001980 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02001981 /* remove all CH clients */
1982 for (i = 0; i < server_opts.ch_client_count; ++i) {
1983 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1984
1985 /* remove all endpoints */
1986 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
1987 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
1988 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
1989 }
1990 free(server_opts.ch_clients[i].ch_endpts);
1991
1992 switch (server_opts.ch_clients[i].ti) {
1993#ifdef NC_ENABLED_SSH
1994 case NC_TI_LIBSSH:
1995 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1996 free(server_opts.ch_clients[i].opts.ssh);
1997 break;
1998#endif
1999#ifdef NC_ENABLED_TLS
2000 case NC_TI_OPENSSL:
2001 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2002 free(server_opts.ch_clients[i].opts.tls);
2003 break;
2004#endif
2005 default:
2006 ERRINT;
2007 /* won't get here ...*/
2008 break;
2009 }
2010
2011 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2012
2013 ret = 0;
2014 }
2015 free(server_opts.ch_clients);
2016 server_opts.ch_clients = NULL;
2017
2018 server_opts.ch_client_count = 0;
2019
2020 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002021 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002022 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002023 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 +02002024 /* remove endpt */
2025 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2026
2027 switch (server_opts.ch_clients[i].ti) {
2028#ifdef NC_ENABLED_SSH
2029 case NC_TI_LIBSSH:
2030 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2031 free(server_opts.ch_clients[i].opts.ssh);
2032 break;
2033#endif
2034#ifdef NC_ENABLED_TLS
2035 case NC_TI_OPENSSL:
2036 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2037 free(server_opts.ch_clients[i].opts.tls);
2038 break;
2039#endif
2040 default:
2041 ERRINT;
2042 break;
2043 }
2044
2045 /* remove all endpoints */
2046 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2047 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2048 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2049 }
2050 free(server_opts.ch_clients[i].ch_endpts);
2051
2052 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2053
2054 /* move last client and endpoint(s) to the empty space */
2055 --server_opts.ch_client_count;
2056 if (i < server_opts.ch_client_count) {
2057 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2058 sizeof *server_opts.ch_clients);
2059 } else if (!server_opts.ch_client_count) {
2060 free(server_opts.ch_clients);
2061 server_opts.ch_clients = NULL;
2062 }
2063
2064 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002065 if (name) {
2066 break;
2067 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002068 }
2069 }
2070 }
2071
2072 /* WRITE UNLOCK */
2073 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2074
2075 return ret;
2076}
2077
2078API int
2079nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2080{
2081 uint16_t i;
2082 struct nc_ch_client *client;
2083
2084 if (!client_name) {
2085 ERRARG("client_name");
2086 return -1;
2087 } else if (!endpt_name) {
2088 ERRARG("endpt_name");
2089 return -1;
2090 }
2091
2092 /* LOCK */
2093 client = nc_server_ch_client_lock(client_name, 0, NULL);
2094 if (!client) {
2095 return -1;
2096 }
2097
2098 for (i = 0; i < client->ch_endpt_count; ++i) {
2099 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2100 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2101 /* UNLOCK */
2102 nc_server_ch_client_unlock(client);
2103 return -1;
2104 }
2105 }
2106
2107 ++client->ch_endpt_count;
2108 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2109 if (!client->ch_endpts) {
2110 ERRMEM;
2111 /* UNLOCK */
2112 nc_server_ch_client_unlock(client);
2113 return -1;
2114 }
2115
2116 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2117 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2118 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
2119
2120 /* UNLOCK */
2121 nc_server_ch_client_unlock(client);
2122
2123 return 0;
2124}
2125
2126API int
2127nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2128{
2129 uint16_t i;
2130 int ret = -1;
2131 struct nc_ch_client *client;
2132
2133 if (!client_name) {
2134 ERRARG("client_name");
2135 return -1;
2136 }
2137
2138 /* LOCK */
2139 client = nc_server_ch_client_lock(client_name, 0, NULL);
2140 if (!client) {
2141 return -1;
2142 }
2143
2144 if (!endpt_name) {
2145 /* remove all endpoints */
2146 for (i = 0; i < client->ch_endpt_count; ++i) {
2147 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2148 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2149 }
2150 free(client->ch_endpts);
2151 client->ch_endpts = NULL;
2152 client->ch_endpt_count = 0;
2153
2154 ret = 0;
2155 } else {
2156 for (i = 0; i < client->ch_endpt_count; ++i) {
2157 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2158 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2159 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002160
Michal Vasko4f921012016-10-20 14:07:45 +02002161 /* move last endpoint to the empty space */
2162 --client->ch_endpt_count;
2163 if (i < client->ch_endpt_count) {
2164 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2165 } else if (!server_opts.ch_client_count) {
2166 free(server_opts.ch_clients);
2167 server_opts.ch_clients = NULL;
2168 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002169
Michal Vasko4f921012016-10-20 14:07:45 +02002170 ret = 0;
2171 break;
2172 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002173 }
2174 }
2175
2176 /* UNLOCK */
2177 nc_server_ch_client_unlock(client);
2178
2179 return ret;
2180}
2181
2182API int
2183nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2184{
2185 uint16_t i;
2186 int ret = -1;
2187 struct nc_ch_client *client;
2188
2189 if (!client_name) {
2190 ERRARG("client_name");
2191 return -1;
2192 } else if (!endpt_name) {
2193 ERRARG("endpt_name");
2194 return -1;
2195 } else if (!address) {
2196 ERRARG("address");
2197 return -1;
2198 }
2199
2200 /* LOCK */
2201 client = nc_server_ch_client_lock(client_name, 0, NULL);
2202 if (!client) {
2203 return -1;
2204 }
2205
2206 for (i = 0; i < client->ch_endpt_count; ++i) {
2207 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2208 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2209 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2210
2211 ret = 0;
2212 break;
2213 }
2214 }
2215
2216 /* UNLOCK */
2217 nc_server_ch_client_unlock(client);
2218
2219 if (ret == -1) {
2220 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2221 }
2222
2223 return ret;
2224}
2225
2226API int
2227nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2228{
2229 uint16_t i;
2230 int ret = -1;
2231 struct nc_ch_client *client;
2232
2233 if (!client_name) {
2234 ERRARG("client_name");
2235 return -1;
2236 } else if (!endpt_name) {
2237 ERRARG("endpt_name");
2238 return -1;
2239 } else if (!port) {
2240 ERRARG("port");
2241 return -1;
2242 }
2243
2244 /* LOCK */
2245 client = nc_server_ch_client_lock(client_name, 0, NULL);
2246 if (!client) {
2247 return -1;
2248 }
2249
2250 for (i = 0; i < client->ch_endpt_count; ++i) {
2251 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2252 client->ch_endpts[i].port = port;
2253
2254 ret = 0;
2255 break;
2256 }
2257 }
2258
2259 /* UNLOCK */
2260 nc_server_ch_client_unlock(client);
2261
2262 if (ret == -1) {
2263 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2264 }
2265
2266 return ret;
2267}
2268
2269API int
2270nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2271{
2272 struct nc_ch_client *client;
2273
2274 if (!client_name) {
2275 ERRARG("client_name");
2276 return -1;
2277 } else if (!conn_type) {
2278 ERRARG("conn_type");
2279 return -1;
2280 }
2281
2282 /* LOCK */
2283 client = nc_server_ch_client_lock(client_name, 0, NULL);
2284 if (!client) {
2285 return -1;
2286 }
2287
2288 if (client->conn_type != conn_type) {
2289 client->conn_type = conn_type;
2290
2291 /* set default options */
2292 switch (conn_type) {
2293 case NC_CH_PERSIST:
2294 client->conn.persist.idle_timeout = 86400;
2295 client->conn.persist.ka_max_wait = 30;
2296 client->conn.persist.ka_max_attempts = 3;
2297 break;
2298 case NC_CH_PERIOD:
2299 client->conn.period.idle_timeout = 300;
2300 client->conn.period.reconnect_timeout = 60;
2301 break;
2302 default:
2303 ERRINT;
2304 break;
2305 }
2306 }
2307
2308 /* UNLOCK */
2309 nc_server_ch_client_unlock(client);
2310
2311 return 0;
2312}
2313
2314API int
2315nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2316{
2317 struct nc_ch_client *client;
2318
2319 if (!client_name) {
2320 ERRARG("client_name");
2321 return -1;
2322 }
2323
2324 /* LOCK */
2325 client = nc_server_ch_client_lock(client_name, 0, NULL);
2326 if (!client) {
2327 return -1;
2328 }
2329
2330 if (client->conn_type != NC_CH_PERSIST) {
2331 ERR("Call Home client \"%s\" is not of persistent connection type.");
2332 /* UNLOCK */
2333 nc_server_ch_client_unlock(client);
2334 return -1;
2335 }
2336
2337 client->conn.persist.idle_timeout = idle_timeout;
2338
2339 /* UNLOCK */
2340 nc_server_ch_client_unlock(client);
2341
2342 return 0;
2343}
2344
2345API int
2346nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2347{
2348 struct nc_ch_client *client;
2349
2350 if (!client_name) {
2351 ERRARG("client_name");
2352 return -1;
2353 } else if (!max_wait) {
2354 ERRARG("max_wait");
2355 return -1;
2356 }
2357
2358 /* LOCK */
2359 client = nc_server_ch_client_lock(client_name, 0, NULL);
2360 if (!client) {
2361 return -1;
2362 }
2363
2364 if (client->conn_type != NC_CH_PERSIST) {
2365 ERR("Call Home client \"%s\" is not of persistent connection type.");
2366 /* UNLOCK */
2367 nc_server_ch_client_unlock(client);
2368 return -1;
2369 }
2370
2371 client->conn.persist.ka_max_wait = max_wait;
2372
2373 /* UNLOCK */
2374 nc_server_ch_client_unlock(client);
2375
2376 return 0;
2377}
2378
2379API int
2380nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2381{
2382 struct nc_ch_client *client;
2383
2384 if (!client_name) {
2385 ERRARG("client_name");
2386 return -1;
2387 }
2388
2389 /* LOCK */
2390 client = nc_server_ch_client_lock(client_name, 0, NULL);
2391 if (!client) {
2392 return -1;
2393 }
2394
2395 if (client->conn_type != NC_CH_PERSIST) {
2396 ERR("Call Home client \"%s\" is not of persistent connection type.");
2397 /* UNLOCK */
2398 nc_server_ch_client_unlock(client);
2399 return -1;
2400 }
2401
2402 client->conn.persist.ka_max_attempts = max_attempts;
2403
2404 /* UNLOCK */
2405 nc_server_ch_client_unlock(client);
2406
2407 return 0;
2408}
2409
2410API int
2411nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2412{
2413 struct nc_ch_client *client;
2414
2415 if (!client_name) {
2416 ERRARG("client_name");
2417 return -1;
2418 }
2419
2420 /* LOCK */
2421 client = nc_server_ch_client_lock(client_name, 0, NULL);
2422 if (!client) {
2423 return -1;
2424 }
2425
2426 if (client->conn_type != NC_CH_PERIOD) {
2427 ERR("Call Home client \"%s\" is not of periodic connection type.");
2428 /* UNLOCK */
2429 nc_server_ch_client_unlock(client);
2430 return -1;
2431 }
2432
2433 client->conn.period.idle_timeout = idle_timeout;
2434
2435 /* UNLOCK */
2436 nc_server_ch_client_unlock(client);
2437
2438 return 0;
2439}
2440
2441API int
2442nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2443{
2444 struct nc_ch_client *client;
2445
2446 if (!client_name) {
2447 ERRARG("client_name");
2448 return -1;
2449 } else if (!reconnect_timeout) {
2450 ERRARG("reconnect_timeout");
2451 return -1;
2452 }
2453
2454 /* LOCK */
2455 client = nc_server_ch_client_lock(client_name, 0, NULL);
2456 if (!client) {
2457 return -1;
2458 }
2459
2460 if (client->conn_type != NC_CH_PERIOD) {
2461 ERR("Call Home client \"%s\" is not of periodic connection type.");
2462 /* UNLOCK */
2463 nc_server_ch_client_unlock(client);
2464 return -1;
2465 }
2466
2467 client->conn.period.reconnect_timeout = reconnect_timeout;
2468
2469 /* UNLOCK */
2470 nc_server_ch_client_unlock(client);
2471
2472 return 0;
2473}
2474
2475API int
2476nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2477{
2478 struct nc_ch_client *client;
2479
2480 if (!client_name) {
2481 ERRARG("client_name");
2482 return -1;
2483 }
2484
2485 /* LOCK */
2486 client = nc_server_ch_client_lock(client_name, 0, NULL);
2487 if (!client) {
2488 return -1;
2489 }
2490
2491 client->start_with = start_with;
2492
2493 /* UNLOCK */
2494 nc_server_ch_client_unlock(client);
2495
2496 return 0;
2497}
2498
2499API int
2500nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2501{
2502 struct nc_ch_client *client;
2503
2504 if (!client_name) {
2505 ERRARG("client_name");
2506 return -1;
2507 } else if (!max_attempts) {
2508 ERRARG("max_attempts");
2509 return -1;
2510 }
2511
2512 /* LOCK */
2513 client = nc_server_ch_client_lock(client_name, 0, NULL);
2514 if (!client) {
2515 return -1;
2516 }
2517
2518 client->max_attempts = max_attempts;
2519
2520 /* UNLOCK */
2521 nc_server_ch_client_unlock(client);
2522
2523 return 0;
2524}
2525
2526/* client lock is expected to be held */
2527static NC_MSG_TYPE
2528nc_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 +01002529{
Michal Vasko71090fc2016-05-24 16:37:28 +02002530 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002531 int sock, ret;
2532
Michal Vasko2e6defd2016-10-07 15:48:15 +02002533 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002534 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002535 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002536 }
2537
Michal Vaskoade892d2017-02-22 13:40:35 +01002538 *session = nc_new_session(0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002539 if (!(*session)) {
2540 ERRMEM;
2541 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002542 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002543 }
2544 (*session)->status = NC_STATUS_STARTING;
2545 (*session)->side = NC_SERVER;
2546 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002547 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002548 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2549 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002550
2551 /* transport lock */
Michal Vaskob05053d2016-01-22 16:12:06 +01002552 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01002553 pthread_cond_init((*session)->ti_cond, NULL);
2554 *(*session)->ti_inuse = 0;
Michal Vaskob05053d2016-01-22 16:12:06 +01002555
2556 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002557#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002558 if (client->ti == NC_TI_LIBSSH) {
2559 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002560 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002561 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002562
Michal Vasko71090fc2016-05-24 16:37:28 +02002563 if (ret < 0) {
2564 msgtype = NC_MSG_ERROR;
2565 goto fail;
2566 } else if (!ret) {
2567 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002568 goto fail;
2569 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002570 } else
2571#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002572#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002573 if (client->ti == NC_TI_OPENSSL) {
2574 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002575 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002576 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002577
Michal Vasko71090fc2016-05-24 16:37:28 +02002578 if (ret < 0) {
2579 msgtype = NC_MSG_ERROR;
2580 goto fail;
2581 } else if (!ret) {
2582 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002583 goto fail;
2584 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002585 } else
2586#endif
2587 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002588 ERRINT;
2589 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002590 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002591 goto fail;
2592 }
2593
2594 /* assign new SID atomically */
2595 /* LOCK */
2596 pthread_spin_lock(&server_opts.sid_lock);
2597 (*session)->id = server_opts.new_session_id++;
2598 /* UNLOCK */
2599 pthread_spin_unlock(&server_opts.sid_lock);
2600
2601 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002602 msgtype = nc_handshake(*session);
2603 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002604 goto fail;
2605 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002606 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002607 (*session)->status = NC_STATUS_RUNNING;
2608
Michal Vasko71090fc2016-05-24 16:37:28 +02002609 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002610
2611fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002612 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002613 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002614 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002615}
2616
Michal Vasko2e6defd2016-10-07 15:48:15 +02002617struct nc_ch_client_thread_arg {
2618 char *client_name;
2619 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2620};
2621
2622static struct nc_ch_client *
2623nc_server_ch_client_with_endpt_lock(const char *name)
2624{
2625 struct nc_ch_client *client;
2626
2627 while (1) {
2628 /* LOCK */
2629 client = nc_server_ch_client_lock(name, 0, NULL);
2630 if (!client) {
2631 return NULL;
2632 }
2633 if (client->ch_endpt_count) {
2634 return client;
2635 }
2636 /* no endpoints defined yet */
2637
2638 /* UNLOCK */
2639 nc_server_ch_client_unlock(client);
2640
2641 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2642 }
2643
2644 return NULL;
2645}
2646
2647static int
2648nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2649{
2650 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002651 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002652 struct timespec ts;
2653 struct nc_ch_client *client;
2654
2655 /* session created, initialize condition */
2656 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2657 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2658 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2659 ERRMEM;
2660 nc_session_free(session, NULL);
2661 return -1;
2662 }
2663 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2664 pthread_cond_init(session->opts.server.ch_cond, NULL);
2665
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002666 session->flags |= NC_SESSION_CALLHOME;
2667
Michal Vasko2e6defd2016-10-07 15:48:15 +02002668 /* CH LOCK */
2669 pthread_mutex_lock(session->opts.server.ch_lock);
2670
2671 /* give the session to the user */
2672 data->session_clb(data->client_name, session);
2673
2674 do {
2675 nc_gettimespec(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01002676 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002677
2678 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2679 if (ret && (ret != ETIMEDOUT)) {
2680 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2681 goto ch_client_remove;
2682 }
2683
2684 /* check whether the client was not removed */
2685 /* LOCK */
2686 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2687 if (!client) {
2688 /* client was removed, finish thread */
2689 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2690 data->client_name);
2691 goto ch_client_remove;
2692 }
2693
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002694 if (client->conn_type == NC_CH_PERSIST) {
2695 /* TODO keep-alives */
2696 idle_timeout = client->conn.persist.idle_timeout;
2697 } else {
2698 idle_timeout = client->conn.period.idle_timeout;
2699 }
2700
Michal Vasko3486a7c2017-03-03 13:28:07 +01002701 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 +02002702 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2703 session->status = NC_STATUS_INVALID;
2704 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2705 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002706
2707 /* UNLOCK */
2708 nc_server_ch_client_unlock(client);
2709
2710 } while (session->status == NC_STATUS_RUNNING);
2711
2712 /* CH UNLOCK */
2713 pthread_mutex_unlock(session->opts.server.ch_lock);
2714
2715 return 0;
2716
2717ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002718 /* make the session a standard one */
2719 pthread_cond_destroy(session->opts.server.ch_cond);
2720 free(session->opts.server.ch_cond);
2721 session->opts.server.ch_cond = NULL;
2722
2723 session->flags &= ~NC_SESSION_CALLHOME;
2724
Michal Vasko2e6defd2016-10-07 15:48:15 +02002725 /* CH UNLOCK */
2726 pthread_mutex_unlock(session->opts.server.ch_lock);
2727
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002728 pthread_mutex_destroy(session->opts.server.ch_lock);
2729 free(session->opts.server.ch_lock);
2730 session->opts.server.ch_lock = NULL;
2731
Michal Vasko2e6defd2016-10-07 15:48:15 +02002732 return 1;
2733}
2734
2735static void *
2736nc_ch_client_thread(void *arg)
2737{
2738 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2739 NC_MSG_TYPE msgtype;
2740 uint8_t cur_attempts = 0;
2741 uint16_t i;
2742 char *cur_endpt_name;
2743 struct nc_ch_endpt *cur_endpt;
2744 struct nc_session *session;
2745 struct nc_ch_client *client;
2746
2747 /* LOCK */
2748 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2749 if (!client) {
2750 goto cleanup;
2751 }
2752
2753 cur_endpt = &client->ch_endpts[0];
2754 cur_endpt_name = strdup(cur_endpt->name);
2755
Michal Vasko29af44b2016-10-13 10:59:55 +02002756 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002757 while (1) {
2758 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2759
2760 if (msgtype == NC_MSG_HELLO) {
2761 /* UNLOCK */
2762 nc_server_ch_client_unlock(client);
2763
Michal Vasko29af44b2016-10-13 10:59:55 +02002764 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002765 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2766 goto cleanup;
2767 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002768 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002769
2770 /* LOCK */
2771 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2772 if (!client) {
2773 goto cleanup;
2774 }
2775
2776 /* session changed status -> it was disconnected for whatever reason,
2777 * persistent connection immediately tries to reconnect, periodic waits some first */
2778 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002779 /* UNLOCK */
2780 nc_server_ch_client_unlock(client);
2781
2782 /* TODO wake up sometimes to check for new notifications */
2783 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2784
2785 /* LOCK */
2786 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2787 if (!client) {
2788 goto cleanup;
2789 }
2790 }
2791
2792 /* set next endpoint to try */
2793 if (client->start_with == NC_CH_FIRST_LISTED) {
2794 cur_endpt = &client->ch_endpts[0];
2795 free(cur_endpt_name);
2796 cur_endpt_name = strdup(cur_endpt->name);
2797 } /* else we keep the current one */
2798 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02002799 /* UNLOCK */
2800 nc_server_ch_client_unlock(client);
2801
Michal Vasko2e6defd2016-10-07 15:48:15 +02002802 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002803 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2804
Michal Vasko6bb116b2016-10-26 13:53:46 +02002805 /* LOCK */
2806 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2807 if (!client) {
2808 goto cleanup;
2809 }
2810
Michal Vasko2e6defd2016-10-07 15:48:15 +02002811 ++cur_attempts;
2812 if (cur_attempts == client->max_attempts) {
2813 for (i = 0; i < client->ch_endpt_count; ++i) {
2814 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2815 break;
2816 }
2817 }
2818 if (i < client->ch_endpt_count - 1) {
2819 /* just go to the next endpoint */
2820 cur_endpt = &client->ch_endpts[i + 1];
2821 free(cur_endpt_name);
2822 cur_endpt_name = strdup(cur_endpt->name);
2823 } else {
2824 /* cur_endpoint was removed or is the last, either way start with the first one */
2825 cur_endpt = &client->ch_endpts[0];
2826 free(cur_endpt_name);
2827 cur_endpt_name = strdup(cur_endpt->name);
2828 }
2829
2830 cur_attempts = 0;
2831 } /* else we keep the current one */
2832 }
2833 }
2834
2835cleanup:
2836 VRB("Call Home client \"%s\" thread exit.", data->client_name);
2837
2838 free(data->client_name);
2839 free(data);
2840 return NULL;
2841}
2842
2843API int
2844nc_connect_ch_client_dispatch(const char *client_name,
2845 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2846 int ret;
2847 pthread_t tid;
2848 struct nc_ch_client_thread_arg *arg;
2849
2850 if (!client_name) {
2851 ERRARG("client_name");
2852 return -1;
2853 } else if (!session_clb) {
2854 ERRARG("session_clb");
2855 return -1;
2856 }
2857
2858 arg = malloc(sizeof *arg);
2859 if (!arg) {
2860 ERRMEM;
2861 return -1;
2862 }
2863 arg->client_name = strdup(client_name);
2864 if (!arg->client_name) {
2865 ERRMEM;
2866 free(arg);
2867 return -1;
2868 }
2869 arg->session_clb = session_clb;
2870
2871 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2872 if (ret) {
2873 ERR("Creating a new thread failed (%s).", strerror(ret));
2874 free(arg->client_name);
2875 free(arg);
2876 return -1;
2877 }
2878 /* the thread now manages arg */
2879
2880 pthread_detach(tid);
2881
2882 return 0;
2883}
2884
Radek Krejci53691be2016-02-22 13:58:37 +01002885#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002886
Michal Vaskoe8e07702017-03-15 10:19:30 +01002887API int
2888nc_server_endpt_count(void)
2889{
2890 return server_opts.endpt_count;
2891}
2892
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002893API time_t
2894nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02002895{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002896 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02002897 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002898 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02002899 }
2900
Michal Vasko2e6defd2016-10-07 15:48:15 +02002901 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02002902}
Michal Vasko3486a7c2017-03-03 13:28:07 +01002903
2904API void
2905nc_session_set_notif_status(struct nc_session *session, int notif_status)
2906{
2907 if (!session || (session->side != NC_SERVER)) {
2908 ERRARG("session");
2909 return;
2910 }
2911
2912 session->opts.server.ntf_status = (notif_status ? 1 : 0);
2913}
2914
2915API int
2916nc_session_get_notif_status(const struct nc_session *session)
2917{
2918 if (!session || (session->side != NC_SERVER)) {
2919 ERRARG("session");
2920 return 0;
2921 }
2922
2923 return session->opts.server.ntf_status;
2924}