blob: 1515fe561f0bcc01ea5e94968185d0f388d568b5 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 server session manipulation functions
5 *
Michal Vasko18aeb5d2017-02-17 09:23:56 +01006 * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01007 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
Miroslav Mareš9563b812017-08-19 17:45:36 +020014#define _GNU_SOURCE /* signals, threads */
Michal Vasko086311b2016-01-08 09:53:11 +010015
16#include <stdint.h>
17#include <stdlib.h>
18#include <errno.h>
19#include <string.h>
20#include <poll.h>
21#include <sys/types.h>
22#include <sys/socket.h>
23#include <netinet/in.h>
24#include <arpa/inet.h>
25#include <unistd.h>
Michal Vasko0190bc32016-03-02 15:47:49 +010026#include <fcntl.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010027#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010028#include <time.h>
Michal Vaskoade892d2017-02-22 13:40:35 +010029#include <signal.h>
Michal Vasko086311b2016-01-08 09:53:11 +010030
Michal Vasko1a38c862016-01-15 15:50:07 +010031#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010032#include "session_server.h"
33
Michal Vaskob48aa812016-01-18 14:13:09 +010034struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010035#ifdef NC_ENABLED_SSH
36 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
37#endif
38 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020039 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
40 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010041};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010042
fanchanghu966f2de2016-07-21 02:28:57 -040043static nc_rpc_clb global_rpc_clb = NULL;
44
Michal Vasko3031aae2016-01-27 16:07:18 +010045struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010046nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010047{
48 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010049 struct nc_endpt *endpt = NULL;
50
Michal Vaskoade892d2017-02-22 13:40:35 +010051 /* WRITE LOCK */
52 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010053
54 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020055 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010056 endpt = &server_opts.endpts[i];
57 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010058 }
59 }
60
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010061 if (!endpt) {
62 ERR("Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010063 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020064 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010065 return NULL;
66 }
67
Michal Vaskoe2713da2016-08-22 16:06:40 +020068 if (idx) {
69 *idx = i;
70 }
71
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010072 return endpt;
73}
74
Michal Vasko2e6defd2016-10-07 15:48:15 +020075struct nc_ch_client *
76nc_server_ch_client_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010077{
Michal Vasko2e6defd2016-10-07 15:48:15 +020078 uint16_t i;
79 struct nc_ch_client *client = NULL;
80
81 /* READ LOCK */
82 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
83
84 for (i = 0; i < server_opts.ch_client_count; ++i) {
85 if (!strcmp(server_opts.ch_clients[i].name, name) && (!ti || (server_opts.ch_clients[i].ti == ti))) {
86 client = &server_opts.ch_clients[i];
87 break;
88 }
89 }
90
91 if (!client) {
92 ERR("Call Home client \"%s\" was not found.", name);
93 /* READ UNLOCK */
94 pthread_rwlock_unlock(&server_opts.ch_client_lock);
95 return NULL;
96 }
97
98 /* CH CLIENT LOCK */
99 pthread_mutex_lock(&client->lock);
100
101 if (idx) {
102 *idx = i;
103 }
104
105 return client;
106}
107
108void
109nc_server_ch_client_unlock(struct nc_ch_client *client)
110{
111 /* CH CLIENT UNLOCK */
112 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100113
114 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200115 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100116}
Michal Vasko086311b2016-01-08 09:53:11 +0100117
Michal Vasko1a38c862016-01-15 15:50:07 +0100118API void
119nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
120{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200121 if (!session) {
122 ERRARG("session");
123 return;
124 } else if (!reason) {
125 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100126 return;
127 }
128
Michal Vasko142cfea2017-08-07 10:12:11 +0200129 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
130 session->killed_by = 0;
131 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100132 session->term_reason = reason;
133}
134
Michal Vasko142cfea2017-08-07 10:12:11 +0200135API void
136nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
137{
138 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
139 ERRARG("session");
140 return;
141 } else if (!sid) {
142 ERRARG("sid");
143 return;
144 }
145
146 session->killed_by = sid;
147}
148
149API void
150nc_session_set_status(struct nc_session *session, NC_STATUS status)
151{
152 if (!session) {
153 ERRARG("session");
154 return;
155 } else if (!status) {
156 ERRARG("status");
157 return;
158 }
159
160 session->status = status;
161}
162
Michal Vasko086311b2016-01-08 09:53:11 +0100163int
Michal Vaskof05562c2016-01-20 12:06:43 +0100164nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100165{
166 const int optVal = 1;
167 const socklen_t optLen = sizeof(optVal);
168 int is_ipv4, sock;
169 struct sockaddr_storage saddr;
170
171 struct sockaddr_in *saddr4;
172 struct sockaddr_in6 *saddr6;
173
174
175 if (!strchr(address, ':')) {
176 is_ipv4 = 1;
177 } else {
178 is_ipv4 = 0;
179 }
180
181 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
182 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100183 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100184 goto fail;
185 }
186
187 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100188 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100189 goto fail;
190 }
191
192 bzero(&saddr, sizeof(struct sockaddr_storage));
193 if (is_ipv4) {
194 saddr4 = (struct sockaddr_in *)&saddr;
195
196 saddr4->sin_family = AF_INET;
197 saddr4->sin_port = htons(port);
198
199 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100200 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100201 goto fail;
202 }
203
204 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100205 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100206 goto fail;
207 }
208
209 } else {
210 saddr6 = (struct sockaddr_in6 *)&saddr;
211
212 saddr6->sin6_family = AF_INET6;
213 saddr6->sin6_port = htons(port);
214
215 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100216 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100217 goto fail;
218 }
219
220 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100221 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100222 goto fail;
223 }
224 }
225
Michal Vaskofb89d772016-01-08 12:25:35 +0100226 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100227 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100228 goto fail;
229 }
230
231 return sock;
232
233fail:
234 if (sock > -1) {
235 close(sock);
236 }
237
238 return -1;
239}
240
241int
Michal Vasko3031aae2016-01-27 16:07:18 +0100242nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx)
Michal Vasko086311b2016-01-08 09:53:11 +0100243{
Michal Vaskof54cd352017-02-22 13:42:02 +0100244 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100245 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100246 struct pollfd *pfd;
247 struct sockaddr_storage saddr;
248 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100249 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100250
251 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100252 if (!pfd) {
253 ERRMEM;
254 return -1;
255 }
256
Michal Vaskoac2f6182017-01-30 14:32:03 +0100257 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200258 if (binds[i].sock < 0) {
259 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200260 continue;
261 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200262 if (binds[i].pollin) {
263 binds[i].pollin = 0;
264 /* leftover pollin */
265 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100266 break;
267 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100268 pfd[pfd_count].fd = binds[i].sock;
269 pfd[pfd_count].events = POLLIN;
270 pfd[pfd_count].revents = 0;
271
272 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100273 }
274
Michal Vasko0a3f3752016-10-13 14:58:38 +0200275 if (sock == -1) {
276 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100277 sigfillset(&sigmask);
278 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100279 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100280 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
281
Michal Vasko0a3f3752016-10-13 14:58:38 +0200282 if (!ret) {
283 /* we timeouted */
284 free(pfd);
285 return 0;
286 } else if (ret == -1) {
287 ERR("Poll failed (%s).", strerror(errno));
288 free(pfd);
289 return -1;
290 }
Michal Vasko086311b2016-01-08 09:53:11 +0100291
Michal Vaskoac2f6182017-01-30 14:32:03 +0100292 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
293 /* adjust i so that indices in binds and pfd always match */
294 while (binds[i].sock != pfd[j].fd) {
295 ++i;
296 }
297
298 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200299 --ret;
300
301 if (!ret) {
302 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100303 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200304 break;
305 } else {
306 /* just remember the event for next time */
307 binds[i].pollin = 1;
308 }
309 }
Michal Vasko086311b2016-01-08 09:53:11 +0100310 }
311 }
312 free(pfd);
313
314 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100315 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100316 return -1;
317 }
318
319 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100320 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100321 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100322 return -1;
323 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200324 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100325
Michal Vasko0190bc32016-03-02 15:47:49 +0100326 /* make the socket non-blocking */
327 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
328 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100329 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100330 return -1;
331 }
332
Michal Vasko3031aae2016-01-27 16:07:18 +0100333 if (idx) {
334 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100335 }
336
Michal Vasko086311b2016-01-08 09:53:11 +0100337 /* host was requested */
338 if (host) {
339 if (saddr.ss_family == AF_INET) {
340 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100341 if (*host) {
342 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
343 ERR("inet_ntop failed (%s).", strerror(errno));
344 free(*host);
345 *host = NULL;
346 }
Michal Vasko086311b2016-01-08 09:53:11 +0100347
Michal Vasko4eb3c312016-03-01 14:09:37 +0100348 if (port) {
349 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
350 }
351 } else {
352 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100353 }
354 } else if (saddr.ss_family == AF_INET6) {
Jan Kundrát0f942e82018-02-14 14:52:00 +0100355 *host = malloc(INET6_ADDRSTRLEN);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100356 if (*host) {
357 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
358 ERR("inet_ntop failed (%s).", strerror(errno));
359 free(*host);
360 *host = NULL;
361 }
Michal Vasko086311b2016-01-08 09:53:11 +0100362
Michal Vasko4eb3c312016-03-01 14:09:37 +0100363 if (port) {
364 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
365 }
366 } else {
367 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100368 }
369 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100370 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100371 }
372 }
373
374 return ret;
375}
376
Michal Vasko05ba9df2016-01-13 14:40:27 +0100377static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100378nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100379{
380 const char *identifier = NULL, *version = NULL, *format = NULL;
381 char *model_data = NULL;
382 const struct lys_module *module;
383 struct nc_server_error *err;
384 struct lyd_node *child, *data = NULL;
Michal Vasko88639e92017-08-03 14:38:10 +0200385 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100386
387 LY_TREE_FOR(rpc->child, child) {
388 if (!strcmp(child->schema->name, "identifier")) {
389 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
390 } else if (!strcmp(child->schema->name, "version")) {
391 version = ((struct lyd_node_leaf_list *)child)->value_str;
Radek Krejci1afa7792017-03-26 11:24:16 -0500392 if (version && version[0] == '\0') {
393 version = NULL;
394 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100395 } else if (!strcmp(child->schema->name, "format")) {
396 format = ((struct lyd_node_leaf_list *)child)->value_str;
397 }
398 }
399
400 /* check version */
401 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100402 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
403 nc_err_set_msg(err, "The requested version is not supported.", "en");
404 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100405 }
406
407 /* check and get module with the name identifier */
Radek Krejci3222b7d2017-09-21 16:04:30 +0200408 module = ly_ctx_get_module(server_opts.ctx, identifier, version, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100409 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200410 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
411 }
412 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100413 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
414 nc_err_set_msg(err, "The requested schema was not found.", "en");
415 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100416 }
417
418 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100419 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100420 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL, 0, 0);
Radek Krejci90fba642016-12-07 15:59:45 +0100421 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vaskof8aa9972018-01-31 13:19:08 +0100422 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL, 0, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100423 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100424 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
425 nc_err_set_msg(err, "The requested format is not supported.", "en");
426 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100427 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200428 if (!model_data) {
429 ERRINT;
430 return NULL;
431 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100432
Michal Vasko88639e92017-08-03 14:38:10 +0200433 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/data", 1);
434 if (!sdata) {
435 ERRINT;
436 free(model_data);
437 return NULL;
438 }
439
Radek Krejci539efb62016-08-24 15:05:16 +0200440 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
441 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100442 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100443 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200444 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100445 return NULL;
446 }
447
Radek Krejci36dfdb32016-09-01 16:56:35 +0200448 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100449}
450
451static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100452nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100453{
Michal Vasko428087d2016-01-14 16:04:28 +0100454 session->term_reason = NC_SESSION_TERM_CLOSED;
455 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100456}
457
Michal Vasko086311b2016-01-08 09:53:11 +0100458API int
459nc_server_init(struct ly_ctx *ctx)
460{
Michal Vasko88639e92017-08-03 14:38:10 +0200461 const struct lys_node *rpc;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100462
Michal Vasko086311b2016-01-08 09:53:11 +0100463 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200464 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100465 return -1;
466 }
467
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100468 nc_init();
469
Michal Vasko05ba9df2016-01-13 14:40:27 +0100470 /* set default <get-schema> callback if not specified */
Michal Vasko88639e92017-08-03 14:38:10 +0200471 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
472 if (rpc && !rpc->priv) {
473 lys_set_private(rpc, nc_clb_default_get_schema);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100474 }
475
476 /* set default <close-session> callback if not specififed */
Michal Vasko88639e92017-08-03 14:38:10 +0200477 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session", 0);
478 if (rpc && !rpc->priv) {
479 lys_set_private(rpc, nc_clb_default_close_session);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100480 }
481
Michal Vasko086311b2016-01-08 09:53:11 +0100482 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100483
484 server_opts.new_session_id = 1;
485 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
486
Michal Vasko086311b2016-01-08 09:53:11 +0100487 return 0;
488}
489
Michal Vaskob48aa812016-01-18 14:13:09 +0100490API void
491nc_server_destroy(void)
492{
Radek Krejci658782b2016-12-04 22:04:55 +0100493 unsigned int i;
494
495 for (i = 0; i < server_opts.capabilities_count; i++) {
496 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
497 }
498 free(server_opts.capabilities);
Michal Vaskob48aa812016-01-18 14:13:09 +0100499 pthread_spin_destroy(&server_opts.sid_lock);
500
Radek Krejci53691be2016-02-22 13:58:37 +0100501#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100502 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100503#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100504#ifdef NC_ENABLED_SSH
505 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100506
507 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
508 server_opts.hostkey_data_free(server_opts.hostkey_data);
509 }
510#endif
511#ifdef NC_ENABLED_TLS
512 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
513 server_opts.server_cert_data_free(server_opts.server_cert_data);
514 }
515 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
516 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
517 }
Michal Vaskob48aa812016-01-18 14:13:09 +0100518#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100519 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100520}
521
Michal Vasko086311b2016-01-08 09:53:11 +0100522API int
523nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
524{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200525 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
526 ERRARG("basic_mode");
527 return -1;
528 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
529 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100530 return -1;
531 }
532
533 server_opts.wd_basic_mode = basic_mode;
534 server_opts.wd_also_supported = also_supported;
535 return 0;
536}
537
Michal Vasko1a38c862016-01-15 15:50:07 +0100538API void
Michal Vasko55f03972016-04-13 08:56:01 +0200539nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
540{
541 if (!basic_mode && !also_supported) {
542 ERRARG("basic_mode and also_supported");
543 return;
544 }
545
546 if (basic_mode) {
547 *basic_mode = server_opts.wd_basic_mode;
548 }
549 if (also_supported) {
550 *also_supported = server_opts.wd_also_supported;
551 }
552}
553
Michal Vasko55f03972016-04-13 08:56:01 +0200554API int
Radek Krejci658782b2016-12-04 22:04:55 +0100555nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200556{
Radek Krejci658782b2016-12-04 22:04:55 +0100557 const char **new;
558
559 if (!value || !value[0]) {
560 ERRARG("value must not be empty");
561 return EXIT_FAILURE;
562 }
563
564 server_opts.capabilities_count++;
565 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
566 if (!new) {
567 ERRMEM;
568 return EXIT_FAILURE;
569 }
570 server_opts.capabilities = new;
571 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
572
573 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200574}
575
Michal Vasko1a38c862016-01-15 15:50:07 +0100576API void
Michal Vasko086311b2016-01-08 09:53:11 +0100577nc_server_set_hello_timeout(uint16_t hello_timeout)
578{
Michal Vasko086311b2016-01-08 09:53:11 +0100579 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100580}
581
Michal Vasko55f03972016-04-13 08:56:01 +0200582API uint16_t
583nc_server_get_hello_timeout(void)
584{
585 return server_opts.hello_timeout;
586}
587
Michal Vasko1a38c862016-01-15 15:50:07 +0100588API void
Michal Vasko086311b2016-01-08 09:53:11 +0100589nc_server_set_idle_timeout(uint16_t idle_timeout)
590{
Michal Vasko086311b2016-01-08 09:53:11 +0100591 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100592}
593
Michal Vasko55f03972016-04-13 08:56:01 +0200594API uint16_t
595nc_server_get_idle_timeout(void)
596{
597 return server_opts.idle_timeout;
598}
599
Michal Vasko71090fc2016-05-24 16:37:28 +0200600API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100601nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100602{
Michal Vasko71090fc2016-05-24 16:37:28 +0200603 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200604 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200605
Michal Vasko45e53ae2016-04-07 11:46:03 +0200606 if (!server_opts.ctx) {
607 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200608 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200609 } else if (fdin < 0) {
610 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200611 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200612 } else if (fdout < 0) {
613 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200614 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200615 } else if (!username) {
616 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200617 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200618 } else if (!session) {
619 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200620 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100621 }
622
623 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +0100624 *session = nc_new_session(0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100625 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100626 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200627 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100628 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100629 (*session)->status = NC_STATUS_STARTING;
630 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100631
Michal Vaskoade892d2017-02-22 13:40:35 +0100632 /* transport lock */
633 pthread_mutex_init((*session)->ti_lock, NULL);
634 pthread_cond_init((*session)->ti_cond, NULL);
635 *(*session)->ti_inuse = 0;
636
Michal Vasko086311b2016-01-08 09:53:11 +0100637 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100638 (*session)->ti_type = NC_TI_FD;
639 (*session)->ti.fd.in = fdin;
640 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100641
642 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100643 (*session)->flags = NC_SESSION_SHAREDCTX;
644 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100645
Michal Vaskob48aa812016-01-18 14:13:09 +0100646 /* assign new SID atomically */
647 pthread_spin_lock(&server_opts.sid_lock);
648 (*session)->id = server_opts.new_session_id++;
649 pthread_spin_unlock(&server_opts.sid_lock);
650
Michal Vasko086311b2016-01-08 09:53:11 +0100651 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200652 msgtype = nc_handshake(*session);
653 if (msgtype != NC_MSG_HELLO) {
654 nc_session_free(*session, NULL);
655 *session = NULL;
656 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100657 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200658
659 nc_gettimespec_mono(&ts_cur);
660 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
661 nc_gettimespec_real(&ts_cur);
662 (*session)->opts.server.session_start = ts_cur.tv_sec;
663
Michal Vasko1a38c862016-01-15 15:50:07 +0100664 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100665
Michal Vasko71090fc2016-05-24 16:37:28 +0200666 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100667}
Michal Vasko9e036d52016-01-08 10:49:26 +0100668
Michal Vaskob30b99c2016-07-26 11:35:43 +0200669static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100670nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
671{
672 uint8_t q_last;
673
674 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
675 ERRINT;
676 return;
677 }
678
679 /* get a unique queue value (by adding 1 to the last added value, if any) */
680 if (ps->queue_len) {
681 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
682 *id = ps->queue[q_last] + 1;
683 } else {
684 *id = 0;
685 }
686
687 /* add the id into the queue */
688 ++ps->queue_len;
689 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
690 ps->queue[q_last] = *id;
691}
692
693static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200694nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
695{
Michal Vasko74c345f2018-02-07 10:37:11 +0100696 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200697
698 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100699 /* get the actual queue idx */
700 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200701
702 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100703 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200704 /* another equal value, simply cannot be */
705 ERRINT;
706 }
Michal Vaskod8340032018-02-12 14:41:00 +0100707 if (found == 2) {
708 /* move the following values */
709 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
710 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100711 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200712 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100713 if (i == 0) {
714 found = 1;
715 } else {
716 /* this is not okay, our id is in the middle of the queue */
717 found = 2;
718 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200719 }
720 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200721 if (!found) {
722 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100723 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200724 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100725
Michal Vasko103fe632018-02-12 16:37:45 +0100726 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100727 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100728 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100729 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
730 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200731}
732
Michal Vaskof04a52a2016-04-07 10:52:10 +0200733int
Michal Vasko26043172016-07-26 14:08:59 +0200734nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200735{
736 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200737 struct timespec ts;
738
Michal Vasko77a6abe2017-10-05 10:02:20 +0200739 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100740 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200741
742 /* LOCK */
743 ret = pthread_mutex_timedlock(&ps->lock, &ts);
744 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200745 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200746 return -1;
747 }
748
Michal Vasko74c345f2018-02-07 10:37:11 +0100749 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +0100750 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100751 ERR("%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +0100752 pthread_mutex_unlock(&ps->lock);
753 return -1;
754 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100755
756 /* add ourselves into the queue */
757 nc_ps_queue_add_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200758
759 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200760 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200761 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +0100762 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200763
764 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
765 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200766 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200767 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200768 nc_ps_queue_remove_id(ps, *id);
769 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200770 return -1;
771 }
772 }
773
Michal Vaskobe86fe32016-04-07 10:43:03 +0200774 /* UNLOCK */
775 pthread_mutex_unlock(&ps->lock);
776
777 return 0;
778}
779
Michal Vaskof04a52a2016-04-07 10:52:10 +0200780int
Michal Vasko26043172016-07-26 14:08:59 +0200781nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200782{
783 int ret;
784 struct timespec ts;
785
Michal Vasko77a6abe2017-10-05 10:02:20 +0200786 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100787 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200788
789 /* LOCK */
790 ret = pthread_mutex_timedlock(&ps->lock, &ts);
791 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200792 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200793 ret = -1;
794 }
795
Michal Vaskob30b99c2016-07-26 11:35:43 +0200796 /* we must be the first, it was our turn after all, right? */
797 if (ps->queue[ps->queue_begin] != id) {
798 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200799 /* UNLOCK */
800 if (!ret) {
801 pthread_mutex_unlock(&ps->lock);
802 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200803 return -1;
804 }
805
Michal Vaskobe86fe32016-04-07 10:43:03 +0200806 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200807 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200808
809 /* broadcast to all other threads that the queue moved */
810 pthread_cond_broadcast(&ps->cond);
811
Michal Vaskobe86fe32016-04-07 10:43:03 +0200812 /* UNLOCK */
813 if (!ret) {
814 pthread_mutex_unlock(&ps->lock);
815 }
816
817 return ret;
818}
819
Michal Vasko428087d2016-01-14 16:04:28 +0100820API struct nc_pollsession *
821nc_ps_new(void)
822{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100823 struct nc_pollsession *ps;
824
825 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100826 if (!ps) {
827 ERRMEM;
828 return NULL;
829 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200830 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100831 pthread_mutex_init(&ps->lock, NULL);
832
833 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100834}
835
836API void
837nc_ps_free(struct nc_pollsession *ps)
838{
fanchanghu3d4e7212017-08-09 09:42:30 +0800839 uint16_t i;
840
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100841 if (!ps) {
842 return;
843 }
844
Michal Vaskobe86fe32016-04-07 10:43:03 +0200845 if (ps->queue_len) {
846 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
847 }
848
fanchanghu3d4e7212017-08-09 09:42:30 +0800849 for (i = 0; i < ps->session_count; i++) {
850 free(ps->sessions[i]);
851 }
852
Michal Vasko428087d2016-01-14 16:04:28 +0100853 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100854 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200855 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100856
Michal Vasko428087d2016-01-14 16:04:28 +0100857 free(ps);
858}
859
860API int
861nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
862{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200863 uint8_t q_id;
864
Michal Vasko45e53ae2016-04-07 11:46:03 +0200865 if (!ps) {
866 ERRARG("ps");
867 return -1;
868 } else if (!session) {
869 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100870 return -1;
871 }
872
Michal Vasko48a63ed2016-03-01 09:48:21 +0100873 /* 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
Michal Vasko428087d2016-01-14 16:04:28 +0100878 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100879 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +0100880 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100881 ERRMEM;
882 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200883 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100884 return -1;
885 }
fanchanghu3d4e7212017-08-09 09:42:30 +0800886 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
887 if (!ps->sessions[ps->session_count - 1]) {
888 ERRMEM;
889 --ps->session_count;
890 /* UNLOCK */
891 nc_ps_unlock(ps, q_id, __func__);
892 return -1;
893 }
894 ps->sessions[ps->session_count - 1]->session = session;
895 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +0100896
Michal Vasko48a63ed2016-03-01 09:48:21 +0100897 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200898 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100899}
900
Michal Vasko48a63ed2016-03-01 09:48:21 +0100901static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100902_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100903{
904 uint16_t i;
905
Radek Krejcid5f978f2016-03-03 13:14:45 +0100906 if (index >= 0) {
907 i = (uint16_t)index;
908 goto remove;
909 }
Michal Vasko428087d2016-01-14 16:04:28 +0100910 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +0800911 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100912remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100913 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +0800914 if (i <= ps->session_count) {
915 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +0100916 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +0800917 }
918 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +0100919 free(ps->sessions);
920 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +0100921 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +0100922 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100923 return 0;
924 }
925 }
926
Michal Vaskof0537d82016-01-29 14:42:38 +0100927 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100928}
929
Michal Vasko48a63ed2016-03-01 09:48:21 +0100930API int
931nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
932{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200933 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200934 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100935
Michal Vasko45e53ae2016-04-07 11:46:03 +0200936 if (!ps) {
937 ERRARG("ps");
938 return -1;
939 } else if (!session) {
940 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100941 return -1;
942 }
943
944 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200945 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200946 return -1;
947 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100948
Radek Krejcid5f978f2016-03-03 13:14:45 +0100949 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100950
951 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200952 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100953
Michal Vaskobe86fe32016-04-07 10:43:03 +0200954 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100955}
956
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100957API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +0200958nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100959{
960 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100961 struct nc_session *ret = NULL;
962
963 if (!ps) {
964 ERRARG("ps");
965 return NULL;
966 }
967
968 /* LOCK */
969 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
970 return NULL;
971 }
972
Michal Vasko4871c9d2017-10-09 14:48:39 +0200973 if (idx < ps->session_count) {
974 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100975 }
976
977 /* UNLOCK */
978 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
979
980 return ret;
981}
982
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100983API uint16_t
984nc_ps_session_count(struct nc_pollsession *ps)
985{
986 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200987 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100988 return 0;
989 }
990
Michal Vaskof4462fd2017-02-15 14:29:05 +0100991 return ps->session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100992}
993
Michal Vasko71090fc2016-05-24 16:37:28 +0200994/* must be called holding the session lock!
995 * returns: NC_PSPOLL_ERROR,
996 * NC_PSPOLL_BAD_RPC,
997 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
998 * NC_PSPOLL_RPC
999 */
1000static int
Radek Krejci93e80222016-10-03 13:34:25 +02001001nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001002{
1003 struct lyxml_elem *xml = NULL;
1004 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001005 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001006 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001007
Michal Vasko45e53ae2016-04-07 11:46:03 +02001008 if (!session) {
1009 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001010 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001011 } else if (!rpc) {
1012 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001013 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001014 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001015 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001016 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001017 }
1018
1019 msgtype = nc_read_msg(session, &xml);
1020
1021 switch (msgtype) {
1022 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001023 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001024 if (!*rpc) {
1025 ERRMEM;
1026 goto error;
1027 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001028
Radek Krejcif93c7d42016-04-06 13:41:15 +02001029 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001030 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1031 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001032 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001033 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +02001034 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +02001035 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001036 nc_server_reply_free(reply);
1037 if (ret == -1) {
1038 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001039 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001040 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1041 } else {
1042 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001043 }
Michal Vasko428087d2016-01-14 16:04:28 +01001044 (*rpc)->root = xml;
1045 break;
1046 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001047 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001048 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001049 goto error;
1050 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001051 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001052 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001053 goto error;
1054 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001055 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001056 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001057 goto error;
1058 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001059 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +01001060 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
1061 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001062 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001063 break;
1064 }
1065
Michal Vasko71090fc2016-05-24 16:37:28 +02001066 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001067
1068error:
1069 /* cleanup */
1070 lyxml_free(server_opts.ctx, xml);
1071
Michal Vasko71090fc2016-05-24 16:37:28 +02001072 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001073}
1074
fanchanghu966f2de2016-07-21 02:28:57 -04001075API void
1076nc_set_global_rpc_clb(nc_rpc_clb clb)
1077{
1078 global_rpc_clb = clb;
1079}
1080
Radek Krejci93e80222016-10-03 13:34:25 +02001081API NC_MSG_TYPE
1082nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1083{
1084 NC_MSG_TYPE result = NC_MSG_NOTIF;
1085 int ret;
1086
1087 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001088 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001089 ERRARG("session");
1090 return NC_MSG_ERROR;
1091 } else if (!notif || !notif->tree || !notif->eventtime) {
1092 ERRARG("notif");
1093 return NC_MSG_ERROR;
1094 }
1095
1096 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vaskoade892d2017-02-22 13:40:35 +01001097 ret = nc_session_lock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001098 if (ret < 0) {
1099 return NC_MSG_ERROR;
1100 } else if (!ret) {
1101 return NC_MSG_WOULDBLOCK;
1102 }
1103
1104 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1105 if (ret == -1) {
1106 ERR("Session %u: failed to write notification.", session->id);
1107 result = NC_MSG_ERROR;
1108 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001109
1110 nc_session_unlock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001111
1112 return result;
1113}
1114
Michal Vasko71090fc2016-05-24 16:37:28 +02001115/* must be called holding the session lock!
1116 * returns: NC_PSPOLL_ERROR,
1117 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1118 * NC_PSPOLL_REPLY_ERROR,
1119 * 0
1120 */
1121static int
Radek Krejci93e80222016-10-03 13:34:25 +02001122nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001123{
1124 nc_rpc_clb clb;
1125 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001126 struct lys_node *rpc_act = NULL;
1127 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001128 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001129
Michal Vasko4a827e52016-03-03 10:59:00 +01001130 if (!rpc) {
1131 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001132 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001133 }
1134
Michal Vasko90e8e692016-07-13 12:27:57 +02001135 if (rpc->tree->schema->nodetype == LYS_RPC) {
1136 /* RPC */
1137 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001138 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001139 /* action */
1140 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1141 if (elem->schema->nodetype == LYS_ACTION) {
1142 rpc_act = elem->schema;
1143 break;
1144 }
1145 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001146 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001147 if (!rpc_act) {
1148 ERRINT;
1149 return NC_PSPOLL_ERROR;
1150 }
1151 }
1152
1153 if (!rpc_act->priv) {
1154 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001155 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001156 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001157 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001158 reply = clb(rpc->tree, session);
1159 }
1160
1161 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001162 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001163 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001164 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1165 if (reply->type == NC_RPL_ERROR) {
1166 ret |= NC_PSPOLL_REPLY_ERROR;
1167 }
1168 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001169
Michal Vasko71090fc2016-05-24 16:37:28 +02001170 if (r == -1) {
1171 ERR("Session %u: failed to write reply.", session->id);
1172 ret |= NC_PSPOLL_ERROR;
1173 }
Michal Vasko428087d2016-01-14 16:04:28 +01001174
1175 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1176 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1177 session->status = NC_STATUS_INVALID;
1178 }
1179
Michal Vasko71090fc2016-05-24 16:37:28 +02001180 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001181}
1182
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001183/* session must be running and session lock held!
1184 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1185 * NC_PSPOLL_ERROR, (msg filled)
1186 * NC_PSPOLL_TIMEOUT,
1187 * NC_PSPOLL_RPC (some application data available),
1188 * NC_PSPOLL_SSH_CHANNEL,
1189 * NC_PSPOLL_SSH_MSG
1190 */
1191static int
Michal Vasko9fb42272017-10-05 13:50:05 +02001192nc_ps_poll_session(struct nc_session *session, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001193{
Michal Vasko9a327362017-01-11 11:31:46 +01001194 struct pollfd pfd;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001195 int r, ret;
Michal Vasko9a327362017-01-11 11:31:46 +01001196#ifdef NC_ENABLED_SSH
1197 struct nc_session *new;
1198#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001199
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001200 /* check timeout first */
1201 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001202 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001203 sprintf(msg, "session idle timeout elapsed");
1204 session->status = NC_STATUS_INVALID;
1205 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1206 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1207 }
1208
1209 switch (session->ti_type) {
1210#ifdef NC_ENABLED_SSH
1211 case NC_TI_LIBSSH:
1212 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001213 if (r == SSH_EOF) {
1214 sprintf(msg, "SSH channel unexpected EOF");
1215 session->status = NC_STATUS_INVALID;
1216 session->term_reason = NC_SESSION_TERM_DROPPED;
1217 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1218 } else if (r == SSH_ERROR) {
1219 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001220 session->status = NC_STATUS_INVALID;
1221 session->term_reason = NC_SESSION_TERM_OTHER;
1222 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001223 } else if (!r) {
1224 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1225 /* new SSH message */
1226 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1227 if (session->ti.libssh.next) {
1228 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1229 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1230 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1231 /* new NETCONF SSH channel */
1232 ret = NC_PSPOLL_SSH_CHANNEL;
1233 break;
1234 }
1235 }
1236 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001237 break;
1238 }
1239 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001240
Michal Vasko8dcaa882017-10-19 14:28:42 +02001241 /* just some SSH message */
1242 ret = NC_PSPOLL_SSH_MSG;
1243 } else {
1244 ret = NC_PSPOLL_TIMEOUT;
1245 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001246 } else {
1247 /* we have some application data */
1248 ret = NC_PSPOLL_RPC;
1249 }
1250 break;
1251#endif
1252#ifdef NC_ENABLED_TLS
1253 case NC_TI_OPENSSL:
1254 r = SSL_pending(session->ti.tls);
1255 if (!r) {
1256 /* no data pending in the SSL buffer, poll fd */
1257 pfd.fd = SSL_get_rfd(session->ti.tls);
1258 if (pfd.fd < 0) {
1259 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1260 ret = NC_PSPOLL_ERROR;
1261 break;
1262 }
1263 pfd.events = POLLIN;
1264 pfd.revents = 0;
1265 r = poll(&pfd, 1, 0);
1266
1267 if ((r < 0) && (errno != EINTR)) {
1268 sprintf(msg, "poll failed (%s)", strerror(errno));
1269 session->status = NC_STATUS_INVALID;
1270 ret = NC_PSPOLL_ERROR;
1271 } else if (r > 0) {
1272 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1273 sprintf(msg, "communication socket unexpectedly closed");
1274 session->status = NC_STATUS_INVALID;
1275 session->term_reason = NC_SESSION_TERM_DROPPED;
1276 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1277 } else if (pfd.revents & POLLERR) {
1278 sprintf(msg, "communication socket error");
1279 session->status = NC_STATUS_INVALID;
1280 session->term_reason = NC_SESSION_TERM_OTHER;
1281 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1282 } else {
1283 ret = NC_PSPOLL_RPC;
1284 }
1285 } else {
1286 ret = NC_PSPOLL_TIMEOUT;
1287 }
1288 } else {
1289 ret = NC_PSPOLL_RPC;
1290 }
1291 break;
1292#endif
1293 case NC_TI_FD:
1294 pfd.fd = session->ti.fd.in;
1295 pfd.events = POLLIN;
1296 pfd.revents = 0;
1297 r = poll(&pfd, 1, 0);
1298
1299 if ((r < 0) && (errno != EINTR)) {
1300 sprintf(msg, "poll failed (%s)", strerror(errno));
1301 session->status = NC_STATUS_INVALID;
1302 ret = NC_PSPOLL_ERROR;
1303 } else if (r > 0) {
1304 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1305 sprintf(msg, "communication socket unexpectedly closed");
1306 session->status = NC_STATUS_INVALID;
1307 session->term_reason = NC_SESSION_TERM_DROPPED;
1308 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1309 } else if (pfd.revents & POLLERR) {
1310 sprintf(msg, "communication socket error");
1311 session->status = NC_STATUS_INVALID;
1312 session->term_reason = NC_SESSION_TERM_OTHER;
1313 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1314 } else {
1315 ret = NC_PSPOLL_RPC;
1316 }
1317 } else {
1318 ret = NC_PSPOLL_TIMEOUT;
1319 }
1320 break;
1321 case NC_TI_NONE:
1322 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1323 ret = NC_PSPOLL_ERROR;
1324 break;
1325 }
1326
1327 return ret;
1328}
1329
1330API int
1331nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1332{
1333 int ret, r;
1334 uint8_t q_id;
1335 uint16_t i, j;
1336 char msg[256];
1337 struct timespec ts_timeout, ts_cur;
1338 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001339 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001340 struct nc_server_rpc *rpc = NULL;
1341
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001342 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001343 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001344 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001345 }
1346
Michal Vaskoade892d2017-02-22 13:40:35 +01001347 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001348 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001349 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001350 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001351
Michal Vaskoade892d2017-02-22 13:40:35 +01001352 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001353 nc_ps_unlock(ps, q_id, __func__);
1354 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001355 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001356
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001357 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001358 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001359 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001360 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001361 nc_addtimespec(&ts_timeout, timeout);
1362 }
1363
1364 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001365 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001366 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001367 if (ps->last_event_session == ps->session_count - 1) {
1368 i = j = 0;
1369 } else {
1370 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001371 }
Michal Vasko9a327362017-01-11 11:31:46 +01001372 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001373 cur_ps_session = ps->sessions[i];
1374 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001375
Michal Vaskoade892d2017-02-22 13:40:35 +01001376 /* SESSION LOCK */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001377 r = nc_session_lock(cur_session, 0, __func__);
1378 if (r == -1) {
1379 ret = NC_PSPOLL_ERROR;
1380 } else if (r == 1) {
1381 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001382 switch (cur_ps_session->state) {
1383 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001384 if (cur_session->status == NC_STATUS_RUNNING) {
1385 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001386 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001387
1388 ret = nc_ps_poll_session(cur_session, ts_cur.tv_sec, msg);
1389 switch (ret) {
1390 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1391 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001392 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001393 break;
1394 case NC_PSPOLL_ERROR:
1395 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001396 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001397 break;
1398 case NC_PSPOLL_TIMEOUT:
1399#ifdef NC_ENABLED_SSH
1400 case NC_PSPOLL_SSH_CHANNEL:
1401 case NC_PSPOLL_SSH_MSG:
1402#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001403 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001404 break;
1405 case NC_PSPOLL_RPC:
1406 /* let's keep the state busy, we are not done with this session */
1407 break;
1408 }
1409 } else {
1410 /* session is not fine, let the caller know */
1411 ret = NC_PSPOLL_SESSION_TERM;
1412 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1413 ret |= NC_PSPOLL_SESSION_ERROR;
1414 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001415 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001416 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001417 break;
1418 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001419 /* it definitely should not be busy because we have the lock */
1420 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001421 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001422 break;
1423 case NC_PS_STATE_INVALID:
1424 /* we got it locked, but it will be freed, let it be */
1425 ret = NC_PSPOLL_TIMEOUT;
1426 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001427 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001428
1429 /* keep the session locked only in this one case */
1430 if (ret != NC_PSPOLL_RPC) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001431 /* SESSION UNLOCK */
1432 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001433 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001434 } else {
1435 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001436 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001437 }
Michal Vasko428087d2016-01-14 16:04:28 +01001438
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001439 /* something happened */
1440 if (ret != NC_PSPOLL_TIMEOUT) {
1441 break;
1442 }
1443
Michal Vasko9a327362017-01-11 11:31:46 +01001444 if (i == ps->session_count - 1) {
1445 i = 0;
1446 } else {
1447 ++i;
1448 }
1449 } while (i != j);
1450
Michal Vaskoade892d2017-02-22 13:40:35 +01001451 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001452 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001453 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001454 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001455 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001456
1457 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1458 /* final timeout */
1459 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001460 }
Michal Vasko428087d2016-01-14 16:04:28 +01001461 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001462 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001463
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001464 /* do we want to return the session? */
1465 switch (ret) {
1466 case NC_PSPOLL_RPC:
1467 case NC_PSPOLL_SESSION_TERM:
1468 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1469#ifdef NC_ENABLED_SSH
1470 case NC_PSPOLL_SSH_CHANNEL:
1471 case NC_PSPOLL_SSH_MSG:
1472#endif
1473 if (session) {
1474 *session = cur_session;
1475 }
1476 ps->last_event_session = i;
1477 break;
1478 default:
1479 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001480 }
Michal Vasko428087d2016-01-14 16:04:28 +01001481
Michal Vaskoade892d2017-02-22 13:40:35 +01001482 /* PS UNLOCK */
1483 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001484
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001485 /* we have some data available and the session is locked */
1486 if (ret == NC_PSPOLL_RPC) {
1487 ret = nc_server_recv_rpc(cur_session, &rpc);
1488 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1489 if (cur_session->status != NC_STATUS_RUNNING) {
1490 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001491 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001492 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001493 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001494 }
1495 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001496 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001497
1498 /* process RPC, not needed afterwards */
1499 ret |= nc_server_send_reply(cur_session, rpc);
1500 nc_server_rpc_free(rpc, server_opts.ctx);
1501
1502 if (cur_session->status != NC_STATUS_RUNNING) {
1503 ret |= NC_PSPOLL_SESSION_TERM;
1504 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1505 ret |= NC_PSPOLL_SESSION_ERROR;
1506 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001507 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001508 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001509 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001510 }
Michal Vasko428087d2016-01-14 16:04:28 +01001511 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001512
1513 /* SESSION UNLOCK */
1514 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001515 }
1516
Michal Vasko48a63ed2016-03-01 09:48:21 +01001517 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001518}
1519
Michal Vaskod09eae62016-02-01 10:32:52 +01001520API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001521nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001522{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001523 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001524 uint16_t i;
1525 struct nc_session *session;
1526
Michal Vasko9a25e932016-02-01 10:36:42 +01001527 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001528 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001529 return;
1530 }
1531
Michal Vasko48a63ed2016-03-01 09:48:21 +01001532 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001533 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001534 return;
1535 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001536
Michal Vasko48a63ed2016-03-01 09:48:21 +01001537 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001538 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001539 nc_session_free(ps->sessions[i]->session, data_free);
1540 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001541 }
1542 free(ps->sessions);
1543 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001544 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001545 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001546 } else {
1547 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001548 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1549 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001550 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001551 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001552 continue;
1553 }
1554
1555 ++i;
1556 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001557 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001558
1559 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001560 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001561}
1562
Radek Krejci53691be2016-02-22 13:58:37 +01001563#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001564
Michal Vaskoe2713da2016-08-22 16:06:40 +02001565API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001566nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001567{
Michal Vasko3031aae2016-01-27 16:07:18 +01001568 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001569 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001570
Michal Vasko45e53ae2016-04-07 11:46:03 +02001571 if (!name) {
1572 ERRARG("name");
1573 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001574 }
1575
Michal Vaskoade892d2017-02-22 13:40:35 +01001576 /* BIND LOCK */
1577 pthread_mutex_lock(&server_opts.bind_lock);
1578
1579 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001580 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001581
1582 /* check name uniqueness */
1583 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001584 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001585 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001586 ret = -1;
1587 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001588 }
1589 }
1590
Michal Vasko3031aae2016-01-27 16:07:18 +01001591 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001592 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001593 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001594 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001595 ret = -1;
1596 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001597 }
1598 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001599 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001600
Michal Vaskoe2713da2016-08-22 16:06:40 +02001601 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001602 if (!server_opts.binds) {
1603 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001604 ret = -1;
1605 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001606 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001607
Michal Vasko2e6defd2016-10-07 15:48:15 +02001608 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1609 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1610 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001611 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001612
1613 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001614#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001615 case NC_TI_LIBSSH:
1616 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1617 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1618 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001619 ret = -1;
1620 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001621 }
1622 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1623 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1624 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1625 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1626 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001627#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001628#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001629 case NC_TI_OPENSSL:
1630 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1631 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1632 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001633 ret = -1;
1634 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001635 }
1636 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001637#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001638 default:
1639 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001640 ret = -1;
1641 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001642 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001643
Michal Vaskoade892d2017-02-22 13:40:35 +01001644cleanup:
1645 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001646 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001647
Michal Vaskoade892d2017-02-22 13:40:35 +01001648 /* BIND UNLOCK */
1649 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001650
Michal Vaskoade892d2017-02-22 13:40:35 +01001651 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001652}
1653
Michal Vasko3031aae2016-01-27 16:07:18 +01001654int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001655nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001656{
1657 struct nc_endpt *endpt;
1658 struct nc_bind *bind = NULL;
1659 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001660 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001661
Michal Vasko45e53ae2016-04-07 11:46:03 +02001662 if (!endpt_name) {
1663 ERRARG("endpt_name");
1664 return -1;
1665 } else if ((!address && !port) || (address && port)) {
1666 ERRARG("address and port");
1667 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001668 }
1669
Michal Vaskoe2713da2016-08-22 16:06:40 +02001670 if (address) {
1671 set_addr = 1;
1672 } else {
1673 set_addr = 0;
1674 }
1675
Michal Vaskoade892d2017-02-22 13:40:35 +01001676 /* BIND LOCK */
1677 pthread_mutex_lock(&server_opts.bind_lock);
1678
1679 /* ENDPT LOCK */
1680 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001681 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001682 /* BIND UNLOCK */
1683 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001684 return -1;
1685 }
1686
Michal Vaskoe2713da2016-08-22 16:06:40 +02001687 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001688
Michal Vaskoe2713da2016-08-22 16:06:40 +02001689 if (set_addr) {
1690 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001691 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001692 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001693 }
1694
Michal Vaskoe2713da2016-08-22 16:06:40 +02001695 /* we have all the information we need to create a listening socket */
1696 if (address && port) {
1697 /* create new socket, close the old one */
1698 sock = nc_sock_listen(address, port);
1699 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001700 ret = -1;
1701 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001702 }
1703
1704 if (bind->sock > -1) {
1705 close(bind->sock);
1706 }
1707 bind->sock = sock;
1708 } /* else we are just setting address or port */
1709
1710 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001711 lydict_remove(server_opts.ctx, bind->address);
1712 bind->address = lydict_insert(server_opts.ctx, address, 0);
1713 } else {
1714 bind->port = port;
1715 }
1716
Michal Vaskoe2713da2016-08-22 16:06:40 +02001717 if (sock > -1) {
1718#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001719 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001720#elif defined(NC_ENABLED_SSH)
1721 VRB("Listening on %s:%u for SSH connections.", address, port);
1722#else
1723 VRB("Listening on %s:%u for TLS connections.", address, port);
1724#endif
1725 }
1726
Michal Vasko4c1fb492017-01-30 14:31:07 +01001727cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001728 /* ENDPT UNLOCK */
1729 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001730
Michal Vaskoade892d2017-02-22 13:40:35 +01001731 /* BIND UNLOCK */
1732 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001733
Michal Vasko4c1fb492017-01-30 14:31:07 +01001734 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001735}
1736
Michal Vaskoe2713da2016-08-22 16:06:40 +02001737API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001738nc_server_endpt_set_address(const char *endpt_name, const char *address)
1739{
1740 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1741}
1742
1743API int
1744nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1745{
1746 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1747}
1748
1749API int
Michal Vasko59050372016-11-22 14:33:55 +01001750nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001751{
1752 uint32_t i;
1753 int ret = -1;
1754
Michal Vaskoade892d2017-02-22 13:40:35 +01001755 /* BIND LOCK */
1756 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001757
Michal Vaskoade892d2017-02-22 13:40:35 +01001758 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001759 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001760
Michal Vasko59050372016-11-22 14:33:55 +01001761 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001762 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001763 for (i = 0; i < server_opts.endpt_count; ++i) {
1764 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001765 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001766#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001767 case NC_TI_LIBSSH:
1768 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1769 free(server_opts.endpts[i].opts.ssh);
1770 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001771#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001772#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001773 case NC_TI_OPENSSL:
1774 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1775 free(server_opts.endpts[i].opts.tls);
1776 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001777#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001778 default:
1779 ERRINT;
1780 /* won't get here ...*/
1781 break;
1782 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001783 ret = 0;
1784 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001785 free(server_opts.endpts);
1786 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001787
1788 /* remove all binds */
1789 for (i = 0; i < server_opts.endpt_count; ++i) {
1790 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1791 if (server_opts.binds[i].sock > -1) {
1792 close(server_opts.binds[i].sock);
1793 }
1794 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001795 free(server_opts.binds);
1796 server_opts.binds = NULL;
1797
Michal Vasko3031aae2016-01-27 16:07:18 +01001798 server_opts.endpt_count = 0;
1799
Michal Vasko1a38c862016-01-15 15:50:07 +01001800 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001801 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001802 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001803 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001804 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001805 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001806 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001807#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001808 case NC_TI_LIBSSH:
1809 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1810 free(server_opts.endpts[i].opts.ssh);
1811 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001812#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001813#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001814 case NC_TI_OPENSSL:
1815 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1816 free(server_opts.endpts[i].opts.tls);
1817 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001818#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001819 default:
1820 ERRINT;
1821 break;
1822 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001823
Michal Vaskoe2713da2016-08-22 16:06:40 +02001824 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001825 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1826 if (server_opts.binds[i].sock > -1) {
1827 close(server_opts.binds[i].sock);
1828 }
1829
1830 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001831 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001832 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001833 free(server_opts.binds);
1834 server_opts.binds = NULL;
1835 free(server_opts.endpts);
1836 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001837 } else if (i < server_opts.endpt_count) {
1838 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1839 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001840 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001841
1842 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001843 if (name) {
1844 break;
1845 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001846 }
1847 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001848 }
1849
Michal Vaskoade892d2017-02-22 13:40:35 +01001850 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001851 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001852
Michal Vaskoade892d2017-02-22 13:40:35 +01001853 /* BIND UNLOCK */
1854 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001855
1856 return ret;
1857}
1858
Michal Vasko71090fc2016-05-24 16:37:28 +02001859API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001860nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001861{
Michal Vasko71090fc2016-05-24 16:37:28 +02001862 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001863 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001864 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001865 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02001866 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01001867
Michal Vasko45e53ae2016-04-07 11:46:03 +02001868 if (!server_opts.ctx) {
1869 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001870 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001871 } else if (!session) {
1872 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001873 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001874 }
1875
Michal Vaskoade892d2017-02-22 13:40:35 +01001876 /* BIND LOCK */
1877 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001878
1879 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001880 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01001881 /* BIND UNLOCK */
1882 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001883 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001884 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001885
Michal Vaskoe2713da2016-08-22 16:06:40 +02001886 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001887 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001888 /* BIND UNLOCK */
1889 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001890 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001891 if (!ret) {
1892 return NC_MSG_WOULDBLOCK;
1893 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001894 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001895 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001896
1897 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
1898 /* ENDPT READ LOCK */
1899 pthread_rwlock_rdlock(&server_opts.endpt_lock);
1900
1901 /* BIND UNLOCK */
1902 pthread_mutex_unlock(&server_opts.bind_lock);
1903
Michal Vaskob48aa812016-01-18 14:13:09 +01001904 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001905
Michal Vaskoade892d2017-02-22 13:40:35 +01001906 *session = nc_new_session(0);
Michal Vasko686aa312016-01-21 15:58:18 +01001907 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001908 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001909 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001910 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001911 msgtype = NC_MSG_ERROR;
1912 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001913 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001914 (*session)->status = NC_STATUS_STARTING;
1915 (*session)->side = NC_SERVER;
1916 (*session)->ctx = server_opts.ctx;
1917 (*session)->flags = NC_SESSION_SHAREDCTX;
1918 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1919 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001920
1921 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001922 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001923 pthread_cond_init((*session)->ti_cond, NULL);
1924 *(*session)->ti_inuse = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001925
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001926 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001927#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001928 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1929 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001930 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001931 if (ret < 0) {
1932 msgtype = NC_MSG_ERROR;
1933 goto cleanup;
1934 } else if (!ret) {
1935 msgtype = NC_MSG_WOULDBLOCK;
1936 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001937 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001938 } else
1939#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001940#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001941 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1942 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001943 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001944 if (ret < 0) {
1945 msgtype = NC_MSG_ERROR;
1946 goto cleanup;
1947 } else if (!ret) {
1948 msgtype = NC_MSG_WOULDBLOCK;
1949 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001950 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001951 } else
1952#endif
1953 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001954 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001955 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001956 msgtype = NC_MSG_ERROR;
1957 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001958 }
1959
Michal Vasko2cc4c682016-03-01 09:16:48 +01001960 (*session)->data = NULL;
1961
Michal Vaskoade892d2017-02-22 13:40:35 +01001962 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001963 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001964
Michal Vaskob48aa812016-01-18 14:13:09 +01001965 /* assign new SID atomically */
1966 /* LOCK */
1967 pthread_spin_lock(&server_opts.sid_lock);
1968 (*session)->id = server_opts.new_session_id++;
1969 /* UNLOCK */
1970 pthread_spin_unlock(&server_opts.sid_lock);
1971
Michal Vasko9e036d52016-01-08 10:49:26 +01001972 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001973 msgtype = nc_handshake(*session);
1974 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001975 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001976 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001977 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001978 }
Michal Vasko9fb42272017-10-05 13:50:05 +02001979
1980 nc_gettimespec_mono(&ts_cur);
1981 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
1982 nc_gettimespec_real(&ts_cur);
1983 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01001984 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001985
Michal Vasko71090fc2016-05-24 16:37:28 +02001986 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001987
Michal Vasko71090fc2016-05-24 16:37:28 +02001988cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001989 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001990 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001991
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001992 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001993 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001994 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001995}
1996
Michal Vasko2e6defd2016-10-07 15:48:15 +02001997API int
1998nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1999{
2000 uint16_t i;
2001
2002 if (!name) {
2003 ERRARG("name");
2004 return -1;
2005 } else if (!ti) {
2006 ERRARG("ti");
2007 return -1;
2008 }
2009
2010 /* WRITE LOCK */
2011 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2012
2013 /* check name uniqueness */
2014 for (i = 0; i < server_opts.ch_client_count; ++i) {
2015 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2016 ERR("Call Home client \"%s\" already exists.", name);
2017 /* WRITE UNLOCK */
2018 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2019 return -1;
2020 }
2021 }
2022
2023 ++server_opts.ch_client_count;
2024 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2025 if (!server_opts.ch_clients) {
2026 ERRMEM;
2027 /* WRITE UNLOCK */
2028 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2029 return -1;
2030 }
2031 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
2032 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002033 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
2034 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002035
2036 switch (ti) {
2037#ifdef NC_ENABLED_SSH
2038 case NC_TI_LIBSSH:
2039 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2040 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
2041 ERRMEM;
2042 /* WRITE UNLOCK */
2043 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2044 return -1;
2045 }
2046 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
2047 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2048 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
2049 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
2050 break;
2051#endif
2052#ifdef NC_ENABLED_TLS
2053 case NC_TI_OPENSSL:
2054 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2055 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
2056 ERRMEM;
2057 /* WRITE UNLOCK */
2058 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2059 return -1;
2060 }
2061 break;
2062#endif
2063 default:
2064 ERRINT;
2065 /* WRITE UNLOCK */
2066 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2067 return -1;
2068 }
2069
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002070 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
2071
Michal Vasko2e6defd2016-10-07 15:48:15 +02002072 /* set CH default options */
2073 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
2074 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
2075
2076 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
2077
2078 /* WRITE UNLOCK */
2079 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2080
2081 return 0;
2082}
2083
2084API int
Michal Vasko59050372016-11-22 14:33:55 +01002085nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002086{
2087 uint16_t i, j;
2088 int ret = -1;
2089
2090 /* WRITE LOCK */
2091 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2092
Michal Vasko59050372016-11-22 14:33:55 +01002093 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002094 /* remove all CH clients */
2095 for (i = 0; i < server_opts.ch_client_count; ++i) {
2096 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2097
2098 /* remove all endpoints */
2099 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2100 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2101 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2102 }
2103 free(server_opts.ch_clients[i].ch_endpts);
2104
2105 switch (server_opts.ch_clients[i].ti) {
2106#ifdef NC_ENABLED_SSH
2107 case NC_TI_LIBSSH:
2108 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2109 free(server_opts.ch_clients[i].opts.ssh);
2110 break;
2111#endif
2112#ifdef NC_ENABLED_TLS
2113 case NC_TI_OPENSSL:
2114 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2115 free(server_opts.ch_clients[i].opts.tls);
2116 break;
2117#endif
2118 default:
2119 ERRINT;
2120 /* won't get here ...*/
2121 break;
2122 }
2123
2124 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2125
2126 ret = 0;
2127 }
2128 free(server_opts.ch_clients);
2129 server_opts.ch_clients = NULL;
2130
2131 server_opts.ch_client_count = 0;
2132
2133 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002134 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002135 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002136 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 +02002137 /* remove endpt */
2138 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2139
2140 switch (server_opts.ch_clients[i].ti) {
2141#ifdef NC_ENABLED_SSH
2142 case NC_TI_LIBSSH:
2143 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2144 free(server_opts.ch_clients[i].opts.ssh);
2145 break;
2146#endif
2147#ifdef NC_ENABLED_TLS
2148 case NC_TI_OPENSSL:
2149 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2150 free(server_opts.ch_clients[i].opts.tls);
2151 break;
2152#endif
2153 default:
2154 ERRINT;
2155 break;
2156 }
2157
2158 /* remove all endpoints */
2159 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2160 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2161 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2162 }
2163 free(server_opts.ch_clients[i].ch_endpts);
2164
2165 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2166
2167 /* move last client and endpoint(s) to the empty space */
2168 --server_opts.ch_client_count;
2169 if (i < server_opts.ch_client_count) {
2170 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2171 sizeof *server_opts.ch_clients);
2172 } else if (!server_opts.ch_client_count) {
2173 free(server_opts.ch_clients);
2174 server_opts.ch_clients = NULL;
2175 }
2176
2177 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002178 if (name) {
2179 break;
2180 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002181 }
2182 }
2183 }
2184
2185 /* WRITE UNLOCK */
2186 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2187
2188 return ret;
2189}
2190
2191API int
2192nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2193{
2194 uint16_t i;
2195 struct nc_ch_client *client;
2196
2197 if (!client_name) {
2198 ERRARG("client_name");
2199 return -1;
2200 } else if (!endpt_name) {
2201 ERRARG("endpt_name");
2202 return -1;
2203 }
2204
2205 /* LOCK */
2206 client = nc_server_ch_client_lock(client_name, 0, NULL);
2207 if (!client) {
2208 return -1;
2209 }
2210
2211 for (i = 0; i < client->ch_endpt_count; ++i) {
2212 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2213 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2214 /* UNLOCK */
2215 nc_server_ch_client_unlock(client);
2216 return -1;
2217 }
2218 }
2219
2220 ++client->ch_endpt_count;
2221 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2222 if (!client->ch_endpts) {
2223 ERRMEM;
2224 /* UNLOCK */
2225 nc_server_ch_client_unlock(client);
2226 return -1;
2227 }
2228
2229 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2230 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2231 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
2232
2233 /* UNLOCK */
2234 nc_server_ch_client_unlock(client);
2235
2236 return 0;
2237}
2238
2239API int
2240nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2241{
2242 uint16_t i;
2243 int ret = -1;
2244 struct nc_ch_client *client;
2245
2246 if (!client_name) {
2247 ERRARG("client_name");
2248 return -1;
2249 }
2250
2251 /* LOCK */
2252 client = nc_server_ch_client_lock(client_name, 0, NULL);
2253 if (!client) {
2254 return -1;
2255 }
2256
2257 if (!endpt_name) {
2258 /* remove all endpoints */
2259 for (i = 0; i < client->ch_endpt_count; ++i) {
2260 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2261 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2262 }
2263 free(client->ch_endpts);
2264 client->ch_endpts = NULL;
2265 client->ch_endpt_count = 0;
2266
2267 ret = 0;
2268 } else {
2269 for (i = 0; i < client->ch_endpt_count; ++i) {
2270 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2271 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2272 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002273
Michal Vasko4f921012016-10-20 14:07:45 +02002274 /* move last endpoint to the empty space */
2275 --client->ch_endpt_count;
2276 if (i < client->ch_endpt_count) {
2277 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2278 } else if (!server_opts.ch_client_count) {
2279 free(server_opts.ch_clients);
2280 server_opts.ch_clients = NULL;
2281 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002282
Michal Vasko4f921012016-10-20 14:07:45 +02002283 ret = 0;
2284 break;
2285 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002286 }
2287 }
2288
2289 /* UNLOCK */
2290 nc_server_ch_client_unlock(client);
2291
2292 return ret;
2293}
2294
2295API int
2296nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2297{
2298 uint16_t i;
2299 int ret = -1;
2300 struct nc_ch_client *client;
2301
2302 if (!client_name) {
2303 ERRARG("client_name");
2304 return -1;
2305 } else if (!endpt_name) {
2306 ERRARG("endpt_name");
2307 return -1;
2308 } else if (!address) {
2309 ERRARG("address");
2310 return -1;
2311 }
2312
2313 /* LOCK */
2314 client = nc_server_ch_client_lock(client_name, 0, NULL);
2315 if (!client) {
2316 return -1;
2317 }
2318
2319 for (i = 0; i < client->ch_endpt_count; ++i) {
2320 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2321 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2322 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2323
2324 ret = 0;
2325 break;
2326 }
2327 }
2328
2329 /* UNLOCK */
2330 nc_server_ch_client_unlock(client);
2331
2332 if (ret == -1) {
2333 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2334 }
2335
2336 return ret;
2337}
2338
2339API int
2340nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2341{
2342 uint16_t i;
2343 int ret = -1;
2344 struct nc_ch_client *client;
2345
2346 if (!client_name) {
2347 ERRARG("client_name");
2348 return -1;
2349 } else if (!endpt_name) {
2350 ERRARG("endpt_name");
2351 return -1;
2352 } else if (!port) {
2353 ERRARG("port");
2354 return -1;
2355 }
2356
2357 /* LOCK */
2358 client = nc_server_ch_client_lock(client_name, 0, NULL);
2359 if (!client) {
2360 return -1;
2361 }
2362
2363 for (i = 0; i < client->ch_endpt_count; ++i) {
2364 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2365 client->ch_endpts[i].port = port;
2366
2367 ret = 0;
2368 break;
2369 }
2370 }
2371
2372 /* UNLOCK */
2373 nc_server_ch_client_unlock(client);
2374
2375 if (ret == -1) {
2376 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2377 }
2378
2379 return ret;
2380}
2381
2382API int
2383nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2384{
2385 struct nc_ch_client *client;
2386
2387 if (!client_name) {
2388 ERRARG("client_name");
2389 return -1;
2390 } else if (!conn_type) {
2391 ERRARG("conn_type");
2392 return -1;
2393 }
2394
2395 /* LOCK */
2396 client = nc_server_ch_client_lock(client_name, 0, NULL);
2397 if (!client) {
2398 return -1;
2399 }
2400
2401 if (client->conn_type != conn_type) {
2402 client->conn_type = conn_type;
2403
2404 /* set default options */
2405 switch (conn_type) {
2406 case NC_CH_PERSIST:
2407 client->conn.persist.idle_timeout = 86400;
2408 client->conn.persist.ka_max_wait = 30;
2409 client->conn.persist.ka_max_attempts = 3;
2410 break;
2411 case NC_CH_PERIOD:
2412 client->conn.period.idle_timeout = 300;
2413 client->conn.period.reconnect_timeout = 60;
2414 break;
2415 default:
2416 ERRINT;
2417 break;
2418 }
2419 }
2420
2421 /* UNLOCK */
2422 nc_server_ch_client_unlock(client);
2423
2424 return 0;
2425}
2426
2427API int
2428nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2429{
2430 struct nc_ch_client *client;
2431
2432 if (!client_name) {
2433 ERRARG("client_name");
2434 return -1;
2435 }
2436
2437 /* LOCK */
2438 client = nc_server_ch_client_lock(client_name, 0, NULL);
2439 if (!client) {
2440 return -1;
2441 }
2442
2443 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002444 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002445 /* UNLOCK */
2446 nc_server_ch_client_unlock(client);
2447 return -1;
2448 }
2449
2450 client->conn.persist.idle_timeout = idle_timeout;
2451
2452 /* UNLOCK */
2453 nc_server_ch_client_unlock(client);
2454
2455 return 0;
2456}
2457
2458API int
2459nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2460{
2461 struct nc_ch_client *client;
2462
2463 if (!client_name) {
2464 ERRARG("client_name");
2465 return -1;
2466 } else if (!max_wait) {
2467 ERRARG("max_wait");
2468 return -1;
2469 }
2470
2471 /* LOCK */
2472 client = nc_server_ch_client_lock(client_name, 0, NULL);
2473 if (!client) {
2474 return -1;
2475 }
2476
2477 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002478 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002479 /* UNLOCK */
2480 nc_server_ch_client_unlock(client);
2481 return -1;
2482 }
2483
2484 client->conn.persist.ka_max_wait = max_wait;
2485
2486 /* UNLOCK */
2487 nc_server_ch_client_unlock(client);
2488
2489 return 0;
2490}
2491
2492API int
2493nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2494{
2495 struct nc_ch_client *client;
2496
2497 if (!client_name) {
2498 ERRARG("client_name");
2499 return -1;
2500 }
2501
2502 /* LOCK */
2503 client = nc_server_ch_client_lock(client_name, 0, NULL);
2504 if (!client) {
2505 return -1;
2506 }
2507
2508 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002509 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002510 /* UNLOCK */
2511 nc_server_ch_client_unlock(client);
2512 return -1;
2513 }
2514
2515 client->conn.persist.ka_max_attempts = max_attempts;
2516
2517 /* UNLOCK */
2518 nc_server_ch_client_unlock(client);
2519
2520 return 0;
2521}
2522
2523API int
2524nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2525{
2526 struct nc_ch_client *client;
2527
2528 if (!client_name) {
2529 ERRARG("client_name");
2530 return -1;
2531 }
2532
2533 /* LOCK */
2534 client = nc_server_ch_client_lock(client_name, 0, NULL);
2535 if (!client) {
2536 return -1;
2537 }
2538
2539 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002540 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002541 /* UNLOCK */
2542 nc_server_ch_client_unlock(client);
2543 return -1;
2544 }
2545
2546 client->conn.period.idle_timeout = idle_timeout;
2547
2548 /* UNLOCK */
2549 nc_server_ch_client_unlock(client);
2550
2551 return 0;
2552}
2553
2554API int
2555nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2556{
2557 struct nc_ch_client *client;
2558
2559 if (!client_name) {
2560 ERRARG("client_name");
2561 return -1;
2562 } else if (!reconnect_timeout) {
2563 ERRARG("reconnect_timeout");
2564 return -1;
2565 }
2566
2567 /* LOCK */
2568 client = nc_server_ch_client_lock(client_name, 0, NULL);
2569 if (!client) {
2570 return -1;
2571 }
2572
2573 if (client->conn_type != NC_CH_PERIOD) {
2574 ERR("Call Home client \"%s\" is not of periodic connection type.");
2575 /* UNLOCK */
2576 nc_server_ch_client_unlock(client);
2577 return -1;
2578 }
2579
2580 client->conn.period.reconnect_timeout = reconnect_timeout;
2581
2582 /* UNLOCK */
2583 nc_server_ch_client_unlock(client);
2584
2585 return 0;
2586}
2587
2588API int
2589nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2590{
2591 struct nc_ch_client *client;
2592
2593 if (!client_name) {
2594 ERRARG("client_name");
2595 return -1;
2596 }
2597
2598 /* LOCK */
2599 client = nc_server_ch_client_lock(client_name, 0, NULL);
2600 if (!client) {
2601 return -1;
2602 }
2603
2604 client->start_with = start_with;
2605
2606 /* UNLOCK */
2607 nc_server_ch_client_unlock(client);
2608
2609 return 0;
2610}
2611
2612API int
2613nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2614{
2615 struct nc_ch_client *client;
2616
2617 if (!client_name) {
2618 ERRARG("client_name");
2619 return -1;
2620 } else if (!max_attempts) {
2621 ERRARG("max_attempts");
2622 return -1;
2623 }
2624
2625 /* LOCK */
2626 client = nc_server_ch_client_lock(client_name, 0, NULL);
2627 if (!client) {
2628 return -1;
2629 }
2630
2631 client->max_attempts = max_attempts;
2632
2633 /* UNLOCK */
2634 nc_server_ch_client_unlock(client);
2635
2636 return 0;
2637}
2638
2639/* client lock is expected to be held */
2640static NC_MSG_TYPE
2641nc_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 +01002642{
Michal Vasko71090fc2016-05-24 16:37:28 +02002643 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002644 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002645 struct timespec ts_cur;
Michal Vaskob05053d2016-01-22 16:12:06 +01002646
Michal Vasko2e6defd2016-10-07 15:48:15 +02002647 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002648 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002649 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002650 }
2651
Michal Vaskoade892d2017-02-22 13:40:35 +01002652 *session = nc_new_session(0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002653 if (!(*session)) {
2654 ERRMEM;
2655 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002656 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002657 }
2658 (*session)->status = NC_STATUS_STARTING;
2659 (*session)->side = NC_SERVER;
2660 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002661 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002662 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2663 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002664
2665 /* transport lock */
Michal Vaskob05053d2016-01-22 16:12:06 +01002666 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01002667 pthread_cond_init((*session)->ti_cond, NULL);
2668 *(*session)->ti_inuse = 0;
Michal Vaskob05053d2016-01-22 16:12:06 +01002669
2670 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002671#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002672 if (client->ti == NC_TI_LIBSSH) {
2673 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002674 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002675 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002676
Michal Vasko71090fc2016-05-24 16:37:28 +02002677 if (ret < 0) {
2678 msgtype = NC_MSG_ERROR;
2679 goto fail;
2680 } else if (!ret) {
2681 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002682 goto fail;
2683 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002684 } else
2685#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002686#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002687 if (client->ti == NC_TI_OPENSSL) {
2688 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002689 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002690 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002691
Michal Vasko71090fc2016-05-24 16:37:28 +02002692 if (ret < 0) {
2693 msgtype = NC_MSG_ERROR;
2694 goto fail;
2695 } else if (!ret) {
2696 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002697 goto fail;
2698 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002699 } else
2700#endif
2701 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002702 ERRINT;
2703 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002704 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002705 goto fail;
2706 }
2707
2708 /* assign new SID atomically */
2709 /* LOCK */
2710 pthread_spin_lock(&server_opts.sid_lock);
2711 (*session)->id = server_opts.new_session_id++;
2712 /* UNLOCK */
2713 pthread_spin_unlock(&server_opts.sid_lock);
2714
2715 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002716 msgtype = nc_handshake(*session);
2717 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002718 goto fail;
2719 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002720
2721 nc_gettimespec_mono(&ts_cur);
2722 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2723 nc_gettimespec_real(&ts_cur);
2724 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01002725 (*session)->status = NC_STATUS_RUNNING;
2726
Michal Vasko71090fc2016-05-24 16:37:28 +02002727 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002728
2729fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002730 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002731 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002732 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002733}
2734
Michal Vasko2e6defd2016-10-07 15:48:15 +02002735struct nc_ch_client_thread_arg {
2736 char *client_name;
2737 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2738};
2739
2740static struct nc_ch_client *
2741nc_server_ch_client_with_endpt_lock(const char *name)
2742{
2743 struct nc_ch_client *client;
2744
2745 while (1) {
2746 /* LOCK */
2747 client = nc_server_ch_client_lock(name, 0, NULL);
2748 if (!client) {
2749 return NULL;
2750 }
2751 if (client->ch_endpt_count) {
2752 return client;
2753 }
2754 /* no endpoints defined yet */
2755
2756 /* UNLOCK */
2757 nc_server_ch_client_unlock(client);
2758
2759 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2760 }
2761
2762 return NULL;
2763}
2764
2765static int
2766nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2767{
2768 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002769 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002770 struct timespec ts;
2771 struct nc_ch_client *client;
2772
2773 /* session created, initialize condition */
2774 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2775 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2776 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2777 ERRMEM;
2778 nc_session_free(session, NULL);
2779 return -1;
2780 }
2781 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2782 pthread_cond_init(session->opts.server.ch_cond, NULL);
2783
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002784 session->flags |= NC_SESSION_CALLHOME;
2785
Michal Vasko2e6defd2016-10-07 15:48:15 +02002786 /* CH LOCK */
2787 pthread_mutex_lock(session->opts.server.ch_lock);
2788
2789 /* give the session to the user */
2790 data->session_clb(data->client_name, session);
2791
2792 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02002793 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01002794 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002795
2796 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2797 if (ret && (ret != ETIMEDOUT)) {
2798 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2799 goto ch_client_remove;
2800 }
2801
2802 /* check whether the client was not removed */
2803 /* LOCK */
2804 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2805 if (!client) {
2806 /* client was removed, finish thread */
2807 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2808 data->client_name);
2809 goto ch_client_remove;
2810 }
2811
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002812 if (client->conn_type == NC_CH_PERSIST) {
2813 /* TODO keep-alives */
2814 idle_timeout = client->conn.persist.idle_timeout;
2815 } else {
2816 idle_timeout = client->conn.period.idle_timeout;
2817 }
2818
Michal Vasko9fb42272017-10-05 13:50:05 +02002819 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01002820 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 +02002821 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2822 session->status = NC_STATUS_INVALID;
2823 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2824 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002825
2826 /* UNLOCK */
2827 nc_server_ch_client_unlock(client);
2828
2829 } while (session->status == NC_STATUS_RUNNING);
2830
2831 /* CH UNLOCK */
2832 pthread_mutex_unlock(session->opts.server.ch_lock);
2833
2834 return 0;
2835
2836ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002837 /* make the session a standard one */
2838 pthread_cond_destroy(session->opts.server.ch_cond);
2839 free(session->opts.server.ch_cond);
2840 session->opts.server.ch_cond = NULL;
2841
2842 session->flags &= ~NC_SESSION_CALLHOME;
2843
Michal Vasko2e6defd2016-10-07 15:48:15 +02002844 /* CH UNLOCK */
2845 pthread_mutex_unlock(session->opts.server.ch_lock);
2846
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002847 pthread_mutex_destroy(session->opts.server.ch_lock);
2848 free(session->opts.server.ch_lock);
2849 session->opts.server.ch_lock = NULL;
2850
Michal Vasko2e6defd2016-10-07 15:48:15 +02002851 return 1;
2852}
2853
2854static void *
2855nc_ch_client_thread(void *arg)
2856{
2857 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2858 NC_MSG_TYPE msgtype;
2859 uint8_t cur_attempts = 0;
2860 uint16_t i;
Michal Vasko9550cf12017-03-21 15:33:58 +01002861 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002862 struct nc_ch_endpt *cur_endpt;
2863 struct nc_session *session;
2864 struct nc_ch_client *client;
2865
2866 /* LOCK */
2867 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2868 if (!client) {
2869 goto cleanup;
2870 }
2871
2872 cur_endpt = &client->ch_endpts[0];
2873 cur_endpt_name = strdup(cur_endpt->name);
2874
Michal Vasko29af44b2016-10-13 10:59:55 +02002875 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002876 while (1) {
2877 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2878
2879 if (msgtype == NC_MSG_HELLO) {
2880 /* UNLOCK */
2881 nc_server_ch_client_unlock(client);
2882
Michal Vasko29af44b2016-10-13 10:59:55 +02002883 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002884 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2885 goto cleanup;
2886 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002887 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002888
2889 /* LOCK */
2890 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2891 if (!client) {
2892 goto cleanup;
2893 }
2894
2895 /* session changed status -> it was disconnected for whatever reason,
2896 * persistent connection immediately tries to reconnect, periodic waits some first */
2897 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002898 /* UNLOCK */
2899 nc_server_ch_client_unlock(client);
2900
2901 /* TODO wake up sometimes to check for new notifications */
2902 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2903
2904 /* LOCK */
2905 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2906 if (!client) {
2907 goto cleanup;
2908 }
2909 }
2910
2911 /* set next endpoint to try */
2912 if (client->start_with == NC_CH_FIRST_LISTED) {
2913 cur_endpt = &client->ch_endpts[0];
2914 free(cur_endpt_name);
2915 cur_endpt_name = strdup(cur_endpt->name);
2916 } /* else we keep the current one */
2917 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02002918 /* UNLOCK */
2919 nc_server_ch_client_unlock(client);
2920
Michal Vasko2e6defd2016-10-07 15:48:15 +02002921 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002922 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2923
Michal Vasko6bb116b2016-10-26 13:53:46 +02002924 /* LOCK */
2925 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2926 if (!client) {
2927 goto cleanup;
2928 }
2929
Michal Vasko2e6defd2016-10-07 15:48:15 +02002930 ++cur_attempts;
2931 if (cur_attempts == client->max_attempts) {
2932 for (i = 0; i < client->ch_endpt_count; ++i) {
2933 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2934 break;
2935 }
2936 }
2937 if (i < client->ch_endpt_count - 1) {
2938 /* just go to the next endpoint */
2939 cur_endpt = &client->ch_endpts[i + 1];
2940 free(cur_endpt_name);
2941 cur_endpt_name = strdup(cur_endpt->name);
2942 } else {
2943 /* cur_endpoint was removed or is the last, either way start with the first one */
2944 cur_endpt = &client->ch_endpts[0];
2945 free(cur_endpt_name);
2946 cur_endpt_name = strdup(cur_endpt->name);
2947 }
2948
2949 cur_attempts = 0;
2950 } /* else we keep the current one */
2951 }
2952 }
2953
2954cleanup:
2955 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01002956 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002957 free(data->client_name);
2958 free(data);
2959 return NULL;
2960}
2961
2962API int
2963nc_connect_ch_client_dispatch(const char *client_name,
2964 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2965 int ret;
2966 pthread_t tid;
2967 struct nc_ch_client_thread_arg *arg;
2968
2969 if (!client_name) {
2970 ERRARG("client_name");
2971 return -1;
2972 } else if (!session_clb) {
2973 ERRARG("session_clb");
2974 return -1;
2975 }
2976
2977 arg = malloc(sizeof *arg);
2978 if (!arg) {
2979 ERRMEM;
2980 return -1;
2981 }
2982 arg->client_name = strdup(client_name);
2983 if (!arg->client_name) {
2984 ERRMEM;
2985 free(arg);
2986 return -1;
2987 }
2988 arg->session_clb = session_clb;
2989
2990 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2991 if (ret) {
2992 ERR("Creating a new thread failed (%s).", strerror(ret));
2993 free(arg->client_name);
2994 free(arg);
2995 return -1;
2996 }
2997 /* the thread now manages arg */
2998
2999 pthread_detach(tid);
3000
3001 return 0;
3002}
3003
Radek Krejci53691be2016-02-22 13:58:37 +01003004#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003005
Michal Vaskoe8e07702017-03-15 10:19:30 +01003006API int
3007nc_server_endpt_count(void)
3008{
3009 return server_opts.endpt_count;
3010}
3011
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003012API time_t
3013nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003014{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003015 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003016 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003017 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003018 }
3019
Michal Vasko2e6defd2016-10-07 15:48:15 +02003020 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003021}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003022
3023API void
3024nc_session_set_notif_status(struct nc_session *session, int notif_status)
3025{
3026 if (!session || (session->side != NC_SERVER)) {
3027 ERRARG("session");
3028 return;
3029 }
3030
3031 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3032}
3033
3034API int
3035nc_session_get_notif_status(const struct nc_session *session)
3036{
3037 if (!session || (session->side != NC_SERVER)) {
3038 ERRARG("session");
3039 return 0;
3040 }
3041
3042 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003043}