blob: bd610d9e93c87be4592f5c39a1a64b884141d2d7 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 server session manipulation functions
5 *
Michal Vasko18aeb5d2017-02-17 09:23:56 +01006 * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01007 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
Michal Vaskoade892d2017-02-22 13:40:35 +010014#define _POSIX_SOUCE /* signals */
Michal Vasko086311b2016-01-08 09:53:11 +010015
16#include <stdint.h>
17#include <stdlib.h>
18#include <errno.h>
19#include <string.h>
20#include <poll.h>
21#include <sys/types.h>
22#include <sys/socket.h>
23#include <netinet/in.h>
24#include <arpa/inet.h>
25#include <unistd.h>
Michal Vasko0190bc32016-03-02 15:47:49 +010026#include <fcntl.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010027#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010028#include <time.h>
Michal Vaskoade892d2017-02-22 13:40:35 +010029#include <signal.h>
Michal Vasko086311b2016-01-08 09:53:11 +010030
Michal Vasko1a38c862016-01-15 15:50:07 +010031#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010032#include "session_server.h"
33
Michal Vaskob48aa812016-01-18 14:13:09 +010034struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010035#ifdef NC_ENABLED_SSH
36 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
37#endif
38 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020039 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
40 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010041};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010042
fanchanghu966f2de2016-07-21 02:28:57 -040043static nc_rpc_clb global_rpc_clb = NULL;
44
Michal Vasko3031aae2016-01-27 16:07:18 +010045struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010046nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010047{
48 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010049 struct nc_endpt *endpt = NULL;
50
Michal Vaskoade892d2017-02-22 13:40:35 +010051 /* WRITE LOCK */
52 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010053
54 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020055 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010056 endpt = &server_opts.endpts[i];
57 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010058 }
59 }
60
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010061 if (!endpt) {
62 ERR("Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010063 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020064 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010065 return NULL;
66 }
67
Michal Vaskoe2713da2016-08-22 16:06:40 +020068 if (idx) {
69 *idx = i;
70 }
71
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010072 return endpt;
73}
74
Michal Vasko2e6defd2016-10-07 15:48:15 +020075struct nc_ch_client *
76nc_server_ch_client_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010077{
Michal Vasko2e6defd2016-10-07 15:48:15 +020078 uint16_t i;
79 struct nc_ch_client *client = NULL;
80
81 /* READ LOCK */
82 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
83
84 for (i = 0; i < server_opts.ch_client_count; ++i) {
85 if (!strcmp(server_opts.ch_clients[i].name, name) && (!ti || (server_opts.ch_clients[i].ti == ti))) {
86 client = &server_opts.ch_clients[i];
87 break;
88 }
89 }
90
91 if (!client) {
92 ERR("Call Home client \"%s\" was not found.", name);
93 /* READ UNLOCK */
94 pthread_rwlock_unlock(&server_opts.ch_client_lock);
95 return NULL;
96 }
97
98 /* CH CLIENT LOCK */
99 pthread_mutex_lock(&client->lock);
100
101 if (idx) {
102 *idx = i;
103 }
104
105 return client;
106}
107
108void
109nc_server_ch_client_unlock(struct nc_ch_client *client)
110{
111 /* CH CLIENT UNLOCK */
112 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100113
114 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200115 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100116}
Michal Vasko086311b2016-01-08 09:53:11 +0100117
Michal Vasko1a38c862016-01-15 15:50:07 +0100118API void
119nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
120{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200121 if (!session) {
122 ERRARG("session");
123 return;
124 } else if (!reason) {
125 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100126 return;
127 }
128
Michal Vasko142cfea2017-08-07 10:12:11 +0200129 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
130 session->killed_by = 0;
131 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100132 session->term_reason = reason;
133}
134
Michal Vasko142cfea2017-08-07 10:12:11 +0200135API void
136nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
137{
138 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
139 ERRARG("session");
140 return;
141 } else if (!sid) {
142 ERRARG("sid");
143 return;
144 }
145
146 session->killed_by = sid;
147}
148
149API void
150nc_session_set_status(struct nc_session *session, NC_STATUS status)
151{
152 if (!session) {
153 ERRARG("session");
154 return;
155 } else if (!status) {
156 ERRARG("status");
157 return;
158 }
159
160 session->status = status;
161}
162
Michal Vasko086311b2016-01-08 09:53:11 +0100163int
Michal Vaskof05562c2016-01-20 12:06:43 +0100164nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100165{
166 const int optVal = 1;
167 const socklen_t optLen = sizeof(optVal);
168 int is_ipv4, sock;
169 struct sockaddr_storage saddr;
170
171 struct sockaddr_in *saddr4;
172 struct sockaddr_in6 *saddr6;
173
174
175 if (!strchr(address, ':')) {
176 is_ipv4 = 1;
177 } else {
178 is_ipv4 = 0;
179 }
180
181 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
182 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100183 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100184 goto fail;
185 }
186
187 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100188 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100189 goto fail;
190 }
191
192 bzero(&saddr, sizeof(struct sockaddr_storage));
193 if (is_ipv4) {
194 saddr4 = (struct sockaddr_in *)&saddr;
195
196 saddr4->sin_family = AF_INET;
197 saddr4->sin_port = htons(port);
198
199 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100200 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100201 goto fail;
202 }
203
204 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100205 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100206 goto fail;
207 }
208
209 } else {
210 saddr6 = (struct sockaddr_in6 *)&saddr;
211
212 saddr6->sin6_family = AF_INET6;
213 saddr6->sin6_port = htons(port);
214
215 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100216 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100217 goto fail;
218 }
219
220 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100221 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100222 goto fail;
223 }
224 }
225
Michal Vaskofb89d772016-01-08 12:25:35 +0100226 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100227 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100228 goto fail;
229 }
230
231 return sock;
232
233fail:
234 if (sock > -1) {
235 close(sock);
236 }
237
238 return -1;
239}
240
241int
Michal Vasko3031aae2016-01-27 16:07:18 +0100242nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx)
Michal Vasko086311b2016-01-08 09:53:11 +0100243{
Michal Vaskof54cd352017-02-22 13:42:02 +0100244 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100245 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100246 struct pollfd *pfd;
247 struct sockaddr_storage saddr;
248 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100249 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100250
251 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100252 if (!pfd) {
253 ERRMEM;
254 return -1;
255 }
256
Michal Vaskoac2f6182017-01-30 14:32:03 +0100257 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200258 if (binds[i].sock < 0) {
259 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200260 continue;
261 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200262 if (binds[i].pollin) {
263 binds[i].pollin = 0;
264 /* leftover pollin */
265 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100266 break;
267 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100268 pfd[pfd_count].fd = binds[i].sock;
269 pfd[pfd_count].events = POLLIN;
270 pfd[pfd_count].revents = 0;
271
272 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100273 }
274
Michal Vasko0a3f3752016-10-13 14:58:38 +0200275 if (sock == -1) {
276 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100277 sigfillset(&sigmask);
278 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100279 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100280 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
281
Michal Vasko0a3f3752016-10-13 14:58:38 +0200282 if (!ret) {
283 /* we timeouted */
284 free(pfd);
285 return 0;
286 } else if (ret == -1) {
287 ERR("Poll failed (%s).", strerror(errno));
288 free(pfd);
289 return -1;
290 }
Michal Vasko086311b2016-01-08 09:53:11 +0100291
Michal Vaskoac2f6182017-01-30 14:32:03 +0100292 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
293 /* adjust i so that indices in binds and pfd always match */
294 while (binds[i].sock != pfd[j].fd) {
295 ++i;
296 }
297
298 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200299 --ret;
300
301 if (!ret) {
302 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100303 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200304 break;
305 } else {
306 /* just remember the event for next time */
307 binds[i].pollin = 1;
308 }
309 }
Michal Vasko086311b2016-01-08 09:53:11 +0100310 }
311 }
312 free(pfd);
313
314 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100315 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100316 return -1;
317 }
318
319 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100320 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100321 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100322 return -1;
323 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200324 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100325
Michal Vasko0190bc32016-03-02 15:47:49 +0100326 /* make the socket non-blocking */
327 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
328 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100329 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100330 return -1;
331 }
332
Michal Vasko3031aae2016-01-27 16:07:18 +0100333 if (idx) {
334 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100335 }
336
Michal Vasko086311b2016-01-08 09:53:11 +0100337 /* host was requested */
338 if (host) {
339 if (saddr.ss_family == AF_INET) {
340 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100341 if (*host) {
342 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
343 ERR("inet_ntop failed (%s).", strerror(errno));
344 free(*host);
345 *host = NULL;
346 }
Michal Vasko086311b2016-01-08 09:53:11 +0100347
Michal Vasko4eb3c312016-03-01 14:09:37 +0100348 if (port) {
349 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
350 }
351 } else {
352 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100353 }
354 } else if (saddr.ss_family == AF_INET6) {
355 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100356 if (*host) {
357 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
358 ERR("inet_ntop failed (%s).", strerror(errno));
359 free(*host);
360 *host = NULL;
361 }
Michal Vasko086311b2016-01-08 09:53:11 +0100362
Michal Vasko4eb3c312016-03-01 14:09:37 +0100363 if (port) {
364 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
365 }
366 } else {
367 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100368 }
369 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100370 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100371 }
372 }
373
374 return ret;
375}
376
Michal Vasko05ba9df2016-01-13 14:40:27 +0100377static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100378nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100379{
380 const char *identifier = NULL, *version = NULL, *format = NULL;
381 char *model_data = NULL;
382 const struct lys_module *module;
383 struct nc_server_error *err;
384 struct lyd_node *child, *data = NULL;
Michal Vasko88639e92017-08-03 14:38:10 +0200385 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100386
387 LY_TREE_FOR(rpc->child, child) {
388 if (!strcmp(child->schema->name, "identifier")) {
389 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
390 } else if (!strcmp(child->schema->name, "version")) {
391 version = ((struct lyd_node_leaf_list *)child)->value_str;
Radek Krejci1afa7792017-03-26 11:24:16 -0500392 if (version && version[0] == '\0') {
393 version = NULL;
394 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100395 } else if (!strcmp(child->schema->name, "format")) {
396 format = ((struct lyd_node_leaf_list *)child)->value_str;
397 }
398 }
399
400 /* check version */
401 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100402 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
403 nc_err_set_msg(err, "The requested version is not supported.", "en");
404 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100405 }
406
407 /* check and get module with the name identifier */
408 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
409 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200410 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
411 }
412 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100413 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
414 nc_err_set_msg(err, "The requested schema was not found.", "en");
415 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100416 }
417
418 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100419 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100420 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
Radek Krejci90fba642016-12-07 15:59:45 +0100421 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100422 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
423 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100424 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
425 nc_err_set_msg(err, "The requested format is not supported.", "en");
426 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100427 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200428 if (!model_data) {
429 ERRINT;
430 return NULL;
431 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100432
Michal Vasko88639e92017-08-03 14:38:10 +0200433 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/data", 1);
434 if (!sdata) {
435 ERRINT;
436 free(model_data);
437 return NULL;
438 }
439
Radek Krejci539efb62016-08-24 15:05:16 +0200440 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
441 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100442 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100443 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200444 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100445 return NULL;
446 }
447
Radek Krejci36dfdb32016-09-01 16:56:35 +0200448 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100449}
450
451static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100452nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100453{
Michal Vasko428087d2016-01-14 16:04:28 +0100454 session->term_reason = NC_SESSION_TERM_CLOSED;
455 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100456}
457
Michal Vasko086311b2016-01-08 09:53:11 +0100458API int
459nc_server_init(struct ly_ctx *ctx)
460{
Michal Vasko88639e92017-08-03 14:38:10 +0200461 const struct lys_node *rpc;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100462
Michal Vasko086311b2016-01-08 09:53:11 +0100463 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200464 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100465 return -1;
466 }
467
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100468 nc_init();
469
Michal Vasko05ba9df2016-01-13 14:40:27 +0100470 /* set default <get-schema> callback if not specified */
Michal Vasko88639e92017-08-03 14:38:10 +0200471 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
472 if (rpc && !rpc->priv) {
473 lys_set_private(rpc, nc_clb_default_get_schema);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100474 }
475
476 /* set default <close-session> callback if not specififed */
Michal Vasko88639e92017-08-03 14:38:10 +0200477 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session", 0);
478 if (rpc && !rpc->priv) {
479 lys_set_private(rpc, nc_clb_default_close_session);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100480 }
481
Michal Vasko086311b2016-01-08 09:53:11 +0100482 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100483
484 server_opts.new_session_id = 1;
485 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
486
Michal Vasko086311b2016-01-08 09:53:11 +0100487 return 0;
488}
489
Michal Vaskob48aa812016-01-18 14:13:09 +0100490API void
491nc_server_destroy(void)
492{
Radek Krejci658782b2016-12-04 22:04:55 +0100493 unsigned int i;
494
495 for (i = 0; i < server_opts.capabilities_count; i++) {
496 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
497 }
498 free(server_opts.capabilities);
Michal Vaskob48aa812016-01-18 14:13:09 +0100499 pthread_spin_destroy(&server_opts.sid_lock);
500
Radek Krejci53691be2016-02-22 13:58:37 +0100501#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100502 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100503#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100504#ifdef NC_ENABLED_SSH
505 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100506
507 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
508 server_opts.hostkey_data_free(server_opts.hostkey_data);
509 }
510#endif
511#ifdef NC_ENABLED_TLS
512 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
513 server_opts.server_cert_data_free(server_opts.server_cert_data);
514 }
515 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
516 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
517 }
Michal Vaskob48aa812016-01-18 14:13:09 +0100518#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100519 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100520}
521
Michal Vasko086311b2016-01-08 09:53:11 +0100522API int
523nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
524{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200525 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
526 ERRARG("basic_mode");
527 return -1;
528 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
529 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100530 return -1;
531 }
532
533 server_opts.wd_basic_mode = basic_mode;
534 server_opts.wd_also_supported = also_supported;
535 return 0;
536}
537
Michal Vasko1a38c862016-01-15 15:50:07 +0100538API void
Michal Vasko55f03972016-04-13 08:56:01 +0200539nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
540{
541 if (!basic_mode && !also_supported) {
542 ERRARG("basic_mode and also_supported");
543 return;
544 }
545
546 if (basic_mode) {
547 *basic_mode = server_opts.wd_basic_mode;
548 }
549 if (also_supported) {
550 *also_supported = server_opts.wd_also_supported;
551 }
552}
553
Michal Vasko55f03972016-04-13 08:56:01 +0200554API int
Radek Krejci658782b2016-12-04 22:04:55 +0100555nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200556{
Radek Krejci658782b2016-12-04 22:04:55 +0100557 const char **new;
558
559 if (!value || !value[0]) {
560 ERRARG("value must not be empty");
561 return EXIT_FAILURE;
562 }
563
564 server_opts.capabilities_count++;
565 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
566 if (!new) {
567 ERRMEM;
568 return EXIT_FAILURE;
569 }
570 server_opts.capabilities = new;
571 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
572
573 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200574}
575
Michal Vasko1a38c862016-01-15 15:50:07 +0100576API void
Michal Vasko086311b2016-01-08 09:53:11 +0100577nc_server_set_hello_timeout(uint16_t hello_timeout)
578{
Michal Vasko086311b2016-01-08 09:53:11 +0100579 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100580}
581
Michal Vasko55f03972016-04-13 08:56:01 +0200582API uint16_t
583nc_server_get_hello_timeout(void)
584{
585 return server_opts.hello_timeout;
586}
587
Michal Vasko1a38c862016-01-15 15:50:07 +0100588API void
Michal Vasko086311b2016-01-08 09:53:11 +0100589nc_server_set_idle_timeout(uint16_t idle_timeout)
590{
Michal Vasko086311b2016-01-08 09:53:11 +0100591 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100592}
593
Michal Vasko55f03972016-04-13 08:56:01 +0200594API uint16_t
595nc_server_get_idle_timeout(void)
596{
597 return server_opts.idle_timeout;
598}
599
Michal Vasko71090fc2016-05-24 16:37:28 +0200600API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100601nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100602{
Michal Vasko71090fc2016-05-24 16:37:28 +0200603 NC_MSG_TYPE msgtype;
604
Michal Vasko45e53ae2016-04-07 11:46:03 +0200605 if (!server_opts.ctx) {
606 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200607 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200608 } else if (fdin < 0) {
609 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200610 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200611 } else if (fdout < 0) {
612 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200613 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200614 } else if (!username) {
615 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200616 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200617 } else if (!session) {
618 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200619 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100620 }
621
622 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +0100623 *session = nc_new_session(0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100624 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100625 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200626 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100627 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100628 (*session)->status = NC_STATUS_STARTING;
629 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100630
Michal Vaskoade892d2017-02-22 13:40:35 +0100631 /* transport lock */
632 pthread_mutex_init((*session)->ti_lock, NULL);
633 pthread_cond_init((*session)->ti_cond, NULL);
634 *(*session)->ti_inuse = 0;
635
Michal Vasko086311b2016-01-08 09:53:11 +0100636 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100637 (*session)->ti_type = NC_TI_FD;
638 (*session)->ti.fd.in = fdin;
639 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100640
641 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100642 (*session)->flags = NC_SESSION_SHAREDCTX;
643 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100644
Michal Vaskob48aa812016-01-18 14:13:09 +0100645 /* assign new SID atomically */
646 pthread_spin_lock(&server_opts.sid_lock);
647 (*session)->id = server_opts.new_session_id++;
648 pthread_spin_unlock(&server_opts.sid_lock);
649
Michal Vasko086311b2016-01-08 09:53:11 +0100650 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200651 msgtype = nc_handshake(*session);
652 if (msgtype != NC_MSG_HELLO) {
653 nc_session_free(*session, NULL);
654 *session = NULL;
655 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100656 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200657 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100658 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100659
Michal Vasko71090fc2016-05-24 16:37:28 +0200660 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100661}
Michal Vasko9e036d52016-01-08 10:49:26 +0100662
Michal Vaskob30b99c2016-07-26 11:35:43 +0200663static void
664nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
665{
666 uint8_t i, found = 0;
667
668 for (i = 0; i < ps->queue_len; ++i) {
669 /* idx round buffer adjust */
670 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
671 i = -ps->queue_begin;
672 }
673
674 if (found) {
675 /* move the value back one place */
676 if (ps->queue[ps->queue_begin + i] == id) {
677 /* another equal value, simply cannot be */
678 ERRINT;
679 }
680
681 if (ps->queue_begin + i == 0) {
682 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
683 } else {
684 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
685 }
686 } else if (ps->queue[ps->queue_begin + i] == id) {
687 /* found our id, there can be no more equal valid values */
688 found = 1;
689 }
690 }
691
692 if (!found) {
693 ERRINT;
694 }
695 --ps->queue_len;
696}
697
Michal Vaskof04a52a2016-04-07 10:52:10 +0200698int
Michal Vasko26043172016-07-26 14:08:59 +0200699nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200700{
701 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200702 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200703 struct timespec ts;
704
Radek Krejci7ac16052016-07-15 11:48:18 +0200705 nc_gettimespec(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100706 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200707
708 /* LOCK */
709 ret = pthread_mutex_timedlock(&ps->lock, &ts);
710 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200711 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200712 return -1;
713 }
714
715 /* get a unique queue value (by adding 1 to the last added value, if any) */
716 if (ps->queue_len) {
717 queue_last = ps->queue_begin + ps->queue_len - 1;
718 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
719 queue_last -= NC_PS_QUEUE_SIZE;
720 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200721 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200722 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200723 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200724 }
725
726 /* add ourselves into the queue */
727 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200728 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200729 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200730 return -1;
731 }
732 ++ps->queue_len;
733 queue_last = ps->queue_begin + ps->queue_len - 1;
734 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
735 queue_last -= NC_PS_QUEUE_SIZE;
736 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200737 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200738
739 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200740 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200741 nc_gettimespec(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100742 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200743
744 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
745 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200746 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200747 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200748 nc_ps_queue_remove_id(ps, *id);
749 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200750 return -1;
751 }
752 }
753
Michal Vaskobe86fe32016-04-07 10:43:03 +0200754 /* UNLOCK */
755 pthread_mutex_unlock(&ps->lock);
756
757 return 0;
758}
759
Michal Vaskof04a52a2016-04-07 10:52:10 +0200760int
Michal Vasko26043172016-07-26 14:08:59 +0200761nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200762{
763 int ret;
764 struct timespec ts;
765
Radek Krejci7ac16052016-07-15 11:48:18 +0200766 nc_gettimespec(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100767 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200768
769 /* LOCK */
770 ret = pthread_mutex_timedlock(&ps->lock, &ts);
771 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200772 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200773 ret = -1;
774 }
775
Michal Vaskob30b99c2016-07-26 11:35:43 +0200776 /* we must be the first, it was our turn after all, right? */
777 if (ps->queue[ps->queue_begin] != id) {
778 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200779 /* UNLOCK */
780 if (!ret) {
781 pthread_mutex_unlock(&ps->lock);
782 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200783 return -1;
784 }
785
Michal Vaskobe86fe32016-04-07 10:43:03 +0200786 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200787 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200788
789 /* broadcast to all other threads that the queue moved */
790 pthread_cond_broadcast(&ps->cond);
791
Michal Vaskobe86fe32016-04-07 10:43:03 +0200792 /* UNLOCK */
793 if (!ret) {
794 pthread_mutex_unlock(&ps->lock);
795 }
796
797 return ret;
798}
799
Michal Vasko428087d2016-01-14 16:04:28 +0100800API struct nc_pollsession *
801nc_ps_new(void)
802{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100803 struct nc_pollsession *ps;
804
805 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100806 if (!ps) {
807 ERRMEM;
808 return NULL;
809 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200810 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100811 pthread_mutex_init(&ps->lock, NULL);
812
813 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100814}
815
816API void
817nc_ps_free(struct nc_pollsession *ps)
818{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100819 if (!ps) {
820 return;
821 }
822
Michal Vaskobe86fe32016-04-07 10:43:03 +0200823 if (ps->queue_len) {
824 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
825 }
826
Michal Vasko428087d2016-01-14 16:04:28 +0100827 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100828 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200829 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100830
Michal Vasko428087d2016-01-14 16:04:28 +0100831 free(ps);
832}
833
834API int
835nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
836{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200837 uint8_t q_id;
838
Michal Vasko45e53ae2016-04-07 11:46:03 +0200839 if (!ps) {
840 ERRARG("ps");
841 return -1;
842 } else if (!session) {
843 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100844 return -1;
845 }
846
Michal Vasko48a63ed2016-03-01 09:48:21 +0100847 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200848 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200849 return -1;
850 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100851
Michal Vasko428087d2016-01-14 16:04:28 +0100852 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100853 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +0100854 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100855 ERRMEM;
856 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200857 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100858 return -1;
859 }
Michal Vaskoe4300a82017-05-24 10:35:42 +0200860 ps->sessions[ps->session_count - 1].session = session;
861 ps->sessions[ps->session_count - 1].state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +0100862
Michal Vasko48a63ed2016-03-01 09:48:21 +0100863 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200864 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100865}
866
Michal Vasko48a63ed2016-03-01 09:48:21 +0100867static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100868_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100869{
870 uint16_t i;
871
Radek Krejcid5f978f2016-03-03 13:14:45 +0100872 if (index >= 0) {
873 i = (uint16_t)index;
874 goto remove;
875 }
Michal Vasko428087d2016-01-14 16:04:28 +0100876 for (i = 0; i < ps->session_count; ++i) {
Michal Vaskoe4300a82017-05-24 10:35:42 +0200877 if (ps->sessions[i].session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100878remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100879 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100880 if (i < ps->session_count) {
881 ps->sessions[i] = ps->sessions[ps->session_count];
Michal Vasko58005732016-02-02 15:50:52 +0100882 } else if (!ps->session_count) {
883 free(ps->sessions);
884 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +0100885 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +0100886 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100887 return 0;
888 }
889 }
890
Michal Vaskof0537d82016-01-29 14:42:38 +0100891 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100892}
893
Michal Vasko48a63ed2016-03-01 09:48:21 +0100894API int
895nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
896{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200897 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200898 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100899
Michal Vasko45e53ae2016-04-07 11:46:03 +0200900 if (!ps) {
901 ERRARG("ps");
902 return -1;
903 } else if (!session) {
904 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100905 return -1;
906 }
907
908 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200909 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200910 return -1;
911 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100912
Radek Krejcid5f978f2016-03-03 13:14:45 +0100913 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100914
915 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200916 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100917
Michal Vaskobe86fe32016-04-07 10:43:03 +0200918 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100919}
920
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100921API struct nc_session *
922nc_ps_get_session_by_sid(const struct nc_pollsession *ps, uint32_t sid)
923{
924 uint8_t q_id;
925 uint16_t i;
926 struct nc_session *ret = NULL;
927
928 if (!ps) {
929 ERRARG("ps");
930 return NULL;
931 }
932
933 /* LOCK */
934 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
935 return NULL;
936 }
937
938 for (i = 0; i < ps->session_count; ++i) {
Michal Vaskoe4300a82017-05-24 10:35:42 +0200939 if (ps->sessions[i].session->id == sid) {
940 ret = ps->sessions[i].session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100941 break;
942 }
943 }
944
945 /* UNLOCK */
946 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
947
948 return ret;
949}
950
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100951API uint16_t
952nc_ps_session_count(struct nc_pollsession *ps)
953{
954 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200955 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100956 return 0;
957 }
958
Michal Vaskof4462fd2017-02-15 14:29:05 +0100959 return ps->session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100960}
961
Michal Vasko71090fc2016-05-24 16:37:28 +0200962/* must be called holding the session lock!
963 * returns: NC_PSPOLL_ERROR,
964 * NC_PSPOLL_BAD_RPC,
965 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
966 * NC_PSPOLL_RPC
967 */
968static int
Radek Krejci93e80222016-10-03 13:34:25 +0200969nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100970{
971 struct lyxml_elem *xml = NULL;
972 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200973 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200974 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100975
Michal Vasko45e53ae2016-04-07 11:46:03 +0200976 if (!session) {
977 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200978 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200979 } else if (!rpc) {
980 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200981 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100982 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100983 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200984 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100985 }
986
987 msgtype = nc_read_msg(session, &xml);
988
989 switch (msgtype) {
990 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200991 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100992 if (!*rpc) {
993 ERRMEM;
994 goto error;
995 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100996
Radek Krejcif93c7d42016-04-06 13:41:15 +0200997 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +0100998 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
999 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001000 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001001 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +02001002 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +02001003 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001004 nc_server_reply_free(reply);
1005 if (ret == -1) {
1006 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001007 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001008 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1009 } else {
1010 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001011 }
Michal Vasko428087d2016-01-14 16:04:28 +01001012 (*rpc)->root = xml;
1013 break;
1014 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001015 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001016 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001017 goto error;
1018 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001019 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001020 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001021 goto error;
1022 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001023 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001024 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001025 goto error;
1026 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001027 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +01001028 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
1029 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001030 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001031 break;
1032 }
1033
Michal Vasko71090fc2016-05-24 16:37:28 +02001034 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001035
1036error:
1037 /* cleanup */
1038 lyxml_free(server_opts.ctx, xml);
1039
Michal Vasko71090fc2016-05-24 16:37:28 +02001040 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001041}
1042
fanchanghu966f2de2016-07-21 02:28:57 -04001043API void
1044nc_set_global_rpc_clb(nc_rpc_clb clb)
1045{
1046 global_rpc_clb = clb;
1047}
1048
Radek Krejci93e80222016-10-03 13:34:25 +02001049API NC_MSG_TYPE
1050nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1051{
1052 NC_MSG_TYPE result = NC_MSG_NOTIF;
1053 int ret;
1054
1055 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001056 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001057 ERRARG("session");
1058 return NC_MSG_ERROR;
1059 } else if (!notif || !notif->tree || !notif->eventtime) {
1060 ERRARG("notif");
1061 return NC_MSG_ERROR;
1062 }
1063
1064 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vaskoade892d2017-02-22 13:40:35 +01001065 ret = nc_session_lock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001066 if (ret < 0) {
1067 return NC_MSG_ERROR;
1068 } else if (!ret) {
1069 return NC_MSG_WOULDBLOCK;
1070 }
1071
1072 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1073 if (ret == -1) {
1074 ERR("Session %u: failed to write notification.", session->id);
1075 result = NC_MSG_ERROR;
1076 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001077
1078 nc_session_unlock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001079
1080 return result;
1081}
1082
Michal Vasko71090fc2016-05-24 16:37:28 +02001083/* must be called holding the session lock!
1084 * returns: NC_PSPOLL_ERROR,
1085 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1086 * NC_PSPOLL_REPLY_ERROR,
1087 * 0
1088 */
1089static int
Radek Krejci93e80222016-10-03 13:34:25 +02001090nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001091{
1092 nc_rpc_clb clb;
1093 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001094 struct lys_node *rpc_act = NULL;
1095 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001096 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001097
Michal Vasko4a827e52016-03-03 10:59:00 +01001098 if (!rpc) {
1099 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001100 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001101 }
1102
Michal Vasko90e8e692016-07-13 12:27:57 +02001103 if (rpc->tree->schema->nodetype == LYS_RPC) {
1104 /* RPC */
1105 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001106 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001107 /* action */
1108 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1109 if (elem->schema->nodetype == LYS_ACTION) {
1110 rpc_act = elem->schema;
1111 break;
1112 }
1113 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001114 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001115 if (!rpc_act) {
1116 ERRINT;
1117 return NC_PSPOLL_ERROR;
1118 }
1119 }
1120
1121 if (!rpc_act->priv) {
1122 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001123 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001124 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001125 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001126 reply = clb(rpc->tree, session);
1127 }
1128
1129 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001130 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001131 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001132 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1133 if (reply->type == NC_RPL_ERROR) {
1134 ret |= NC_PSPOLL_REPLY_ERROR;
1135 }
1136 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001137
Michal Vasko71090fc2016-05-24 16:37:28 +02001138 if (r == -1) {
1139 ERR("Session %u: failed to write reply.", session->id);
1140 ret |= NC_PSPOLL_ERROR;
1141 }
Michal Vasko428087d2016-01-14 16:04:28 +01001142
1143 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1144 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1145 session->status = NC_STATUS_INVALID;
1146 }
1147
Michal Vasko71090fc2016-05-24 16:37:28 +02001148 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001149}
1150
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001151/* session must be running and session lock held!
1152 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1153 * NC_PSPOLL_ERROR, (msg filled)
1154 * NC_PSPOLL_TIMEOUT,
1155 * NC_PSPOLL_RPC (some application data available),
1156 * NC_PSPOLL_SSH_CHANNEL,
1157 * NC_PSPOLL_SSH_MSG
1158 */
1159static int
1160nc_ps_poll_session(struct nc_session *session, time_t now, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001161{
Michal Vasko9a327362017-01-11 11:31:46 +01001162 struct pollfd pfd;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001163 int r, ret;
Michal Vasko9a327362017-01-11 11:31:46 +01001164#ifdef NC_ENABLED_SSH
1165 struct nc_session *new;
1166#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001167
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001168 /* check timeout first */
1169 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
1170 && (now >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
1171 sprintf(msg, "session idle timeout elapsed");
1172 session->status = NC_STATUS_INVALID;
1173 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1174 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1175 }
1176
1177 switch (session->ti_type) {
1178#ifdef NC_ENABLED_SSH
1179 case NC_TI_LIBSSH:
1180 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1181 if (r < 1) {
1182 if (r == SSH_EOF) {
1183 sprintf(msg, "SSH channel unexpected EOF");
1184 session->status = NC_STATUS_INVALID;
1185 session->term_reason = NC_SESSION_TERM_DROPPED;
1186 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1187 } else if (r == SSH_ERROR) {
1188 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
1189 session->status = NC_STATUS_INVALID;
1190 session->term_reason = NC_SESSION_TERM_OTHER;
1191 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1192 } else {
1193 ret = NC_PSPOLL_TIMEOUT;
1194 }
1195 break;
1196 }
1197
1198 /* we have some data, but it may be just an SSH message */
1199 r = ssh_execute_message_callbacks(session->ti.libssh.session);
1200 if (r != SSH_OK) {
1201 sprintf(msg, "failed to receive SSH messages (%s)", ssh_get_error(session->ti.libssh.session));
1202 session->status = NC_STATUS_INVALID;
1203 session->term_reason = NC_SESSION_TERM_OTHER;
1204 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1205 } else if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1206 /* new SSH message */
1207 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1208 if (session->ti.libssh.next) {
1209 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1210 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1211 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1212 /* new NETCONF SSH channel */
1213 ret = NC_PSPOLL_SSH_CHANNEL;
1214 break;
1215 }
1216 }
1217 if (new != session) {
1218 break;
1219 }
1220 }
1221
1222 /* just some SSH message */
1223 ret = NC_PSPOLL_SSH_MSG;
1224 } else {
1225 /* we have some application data */
1226 ret = NC_PSPOLL_RPC;
1227 }
1228 break;
1229#endif
1230#ifdef NC_ENABLED_TLS
1231 case NC_TI_OPENSSL:
1232 r = SSL_pending(session->ti.tls);
1233 if (!r) {
1234 /* no data pending in the SSL buffer, poll fd */
1235 pfd.fd = SSL_get_rfd(session->ti.tls);
1236 if (pfd.fd < 0) {
1237 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1238 ret = NC_PSPOLL_ERROR;
1239 break;
1240 }
1241 pfd.events = POLLIN;
1242 pfd.revents = 0;
1243 r = poll(&pfd, 1, 0);
1244
1245 if ((r < 0) && (errno != EINTR)) {
1246 sprintf(msg, "poll failed (%s)", strerror(errno));
1247 session->status = NC_STATUS_INVALID;
1248 ret = NC_PSPOLL_ERROR;
1249 } else if (r > 0) {
1250 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1251 sprintf(msg, "communication socket unexpectedly closed");
1252 session->status = NC_STATUS_INVALID;
1253 session->term_reason = NC_SESSION_TERM_DROPPED;
1254 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1255 } else if (pfd.revents & POLLERR) {
1256 sprintf(msg, "communication socket error");
1257 session->status = NC_STATUS_INVALID;
1258 session->term_reason = NC_SESSION_TERM_OTHER;
1259 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1260 } else {
1261 ret = NC_PSPOLL_RPC;
1262 }
1263 } else {
1264 ret = NC_PSPOLL_TIMEOUT;
1265 }
1266 } else {
1267 ret = NC_PSPOLL_RPC;
1268 }
1269 break;
1270#endif
1271 case NC_TI_FD:
1272 pfd.fd = session->ti.fd.in;
1273 pfd.events = POLLIN;
1274 pfd.revents = 0;
1275 r = poll(&pfd, 1, 0);
1276
1277 if ((r < 0) && (errno != EINTR)) {
1278 sprintf(msg, "poll failed (%s)", strerror(errno));
1279 session->status = NC_STATUS_INVALID;
1280 ret = NC_PSPOLL_ERROR;
1281 } else if (r > 0) {
1282 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1283 sprintf(msg, "communication socket unexpectedly closed");
1284 session->status = NC_STATUS_INVALID;
1285 session->term_reason = NC_SESSION_TERM_DROPPED;
1286 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1287 } else if (pfd.revents & POLLERR) {
1288 sprintf(msg, "communication socket error");
1289 session->status = NC_STATUS_INVALID;
1290 session->term_reason = NC_SESSION_TERM_OTHER;
1291 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1292 } else {
1293 ret = NC_PSPOLL_RPC;
1294 }
1295 } else {
1296 ret = NC_PSPOLL_TIMEOUT;
1297 }
1298 break;
1299 case NC_TI_NONE:
1300 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1301 ret = NC_PSPOLL_ERROR;
1302 break;
1303 }
1304
1305 return ret;
1306}
1307
1308API int
1309nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1310{
1311 int ret, r;
1312 uint8_t q_id;
1313 uint16_t i, j;
1314 char msg[256];
1315 struct timespec ts_timeout, ts_cur;
1316 struct nc_session *cur_session;
1317 struct nc_server_rpc *rpc = NULL;
1318
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001319 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001320 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001321 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001322 }
1323
Michal Vaskoade892d2017-02-22 13:40:35 +01001324 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001325 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001326 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001327 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001328
Michal Vaskoade892d2017-02-22 13:40:35 +01001329 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001330 nc_ps_unlock(ps, q_id, __func__);
1331 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001332 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001333
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001334 /* fill timespecs */
Michal Vasko36c7be82017-02-22 13:37:59 +01001335 nc_gettimespec(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001336 if (timeout > -1) {
1337 nc_gettimespec(&ts_timeout);
1338 nc_addtimespec(&ts_timeout, timeout);
1339 }
1340
1341 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001342 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001343 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001344 if (ps->last_event_session == ps->session_count - 1) {
1345 i = j = 0;
1346 } else {
1347 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001348 }
Michal Vasko9a327362017-01-11 11:31:46 +01001349 do {
Michal Vaskoe4300a82017-05-24 10:35:42 +02001350 cur_session = ps->sessions[i].session;
Michal Vasko428087d2016-01-14 16:04:28 +01001351
Michal Vaskoade892d2017-02-22 13:40:35 +01001352 /* SESSION LOCK */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001353 r = nc_session_lock(cur_session, 0, __func__);
1354 if (r == -1) {
1355 ret = NC_PSPOLL_ERROR;
1356 } else if (r == 1) {
1357 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vasko88639e92017-08-03 14:38:10 +02001358 if (ps->sessions[i].state == NC_PS_STATE_NONE) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001359 if (cur_session->status == NC_STATUS_RUNNING) {
1360 /* session is fine, work with it */
1361 ps->sessions[i].state = NC_PS_STATE_BUSY;
1362
1363 ret = nc_ps_poll_session(cur_session, ts_cur.tv_sec, msg);
1364 switch (ret) {
1365 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1366 ERR("Session %u: %s.", cur_session->id, msg);
1367 ps->sessions[i].state = NC_PS_STATE_INVALID;
1368 break;
1369 case NC_PSPOLL_ERROR:
1370 ERR("Session %u: %s.", cur_session->id, msg);
1371 ps->sessions[i].state = NC_PS_STATE_NONE;
1372 break;
1373 case NC_PSPOLL_TIMEOUT:
1374#ifdef NC_ENABLED_SSH
1375 case NC_PSPOLL_SSH_CHANNEL:
1376 case NC_PSPOLL_SSH_MSG:
1377#endif
1378 ps->sessions[i].state = NC_PS_STATE_NONE;
1379 break;
1380 case NC_PSPOLL_RPC:
1381 /* let's keep the state busy, we are not done with this session */
1382 break;
1383 }
1384 } else {
1385 /* session is not fine, let the caller know */
1386 ret = NC_PSPOLL_SESSION_TERM;
1387 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1388 ret |= NC_PSPOLL_SESSION_ERROR;
1389 }
1390 ps->sessions[i].state = NC_PS_STATE_INVALID;
1391 }
Michal Vasko88639e92017-08-03 14:38:10 +02001392 } else if (ps->sessions[i].state == NC_PS_STATE_BUSY) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001393 /* it definitely should not be busy because we have the lock */
1394 ERRINT;
Michal Vasko428087d2016-01-14 16:04:28 +01001395 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001396
1397 /* keep the session locked only in this one case */
1398 if (ret != NC_PSPOLL_RPC) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001399 /* SESSION UNLOCK */
1400 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001401 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001402 } else {
1403 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001404 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001405 }
Michal Vasko428087d2016-01-14 16:04:28 +01001406
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001407 /* something happened */
1408 if (ret != NC_PSPOLL_TIMEOUT) {
1409 break;
1410 }
1411
Michal Vasko9a327362017-01-11 11:31:46 +01001412 if (i == ps->session_count - 1) {
1413 i = 0;
1414 } else {
1415 ++i;
1416 }
1417 } while (i != j);
1418
Michal Vaskoade892d2017-02-22 13:40:35 +01001419 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001420 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001421 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001422 /* update current time */
Michal Vaskoade892d2017-02-22 13:40:35 +01001423 nc_gettimespec(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001424
1425 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1426 /* final timeout */
1427 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001428 }
Michal Vasko428087d2016-01-14 16:04:28 +01001429 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001430 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001431
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001432 /* do we want to return the session? */
1433 switch (ret) {
1434 case NC_PSPOLL_RPC:
1435 case NC_PSPOLL_SESSION_TERM:
1436 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1437#ifdef NC_ENABLED_SSH
1438 case NC_PSPOLL_SSH_CHANNEL:
1439 case NC_PSPOLL_SSH_MSG:
1440#endif
1441 if (session) {
1442 *session = cur_session;
1443 }
1444 ps->last_event_session = i;
1445 break;
1446 default:
1447 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001448 }
Michal Vasko428087d2016-01-14 16:04:28 +01001449
Michal Vaskoade892d2017-02-22 13:40:35 +01001450 /* PS UNLOCK */
1451 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001452
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001453 /* we have some data available and the session is locked */
1454 if (ret == NC_PSPOLL_RPC) {
1455 ret = nc_server_recv_rpc(cur_session, &rpc);
1456 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1457 if (cur_session->status != NC_STATUS_RUNNING) {
1458 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1459 ps->sessions[i].state = NC_PS_STATE_INVALID;
1460 } else {
1461 ps->sessions[i].state = NC_PS_STATE_NONE;
1462 }
1463 } else {
1464 cur_session->opts.server.last_rpc = time(NULL);
1465
1466 /* process RPC, not needed afterwards */
1467 ret |= nc_server_send_reply(cur_session, rpc);
1468 nc_server_rpc_free(rpc, server_opts.ctx);
1469
1470 if (cur_session->status != NC_STATUS_RUNNING) {
1471 ret |= NC_PSPOLL_SESSION_TERM;
1472 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1473 ret |= NC_PSPOLL_SESSION_ERROR;
1474 }
1475 ps->sessions[i].state = NC_PS_STATE_INVALID;
1476 } else {
1477 ps->sessions[i].state = NC_PS_STATE_NONE;
1478 }
Michal Vasko428087d2016-01-14 16:04:28 +01001479 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001480
1481 /* SESSION UNLOCK */
1482 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001483 }
1484
Michal Vasko48a63ed2016-03-01 09:48:21 +01001485 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001486}
1487
Michal Vaskod09eae62016-02-01 10:32:52 +01001488API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001489nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001490{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001491 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001492 uint16_t i;
1493 struct nc_session *session;
1494
Michal Vasko9a25e932016-02-01 10:36:42 +01001495 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001496 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001497 return;
1498 }
1499
Michal Vasko48a63ed2016-03-01 09:48:21 +01001500 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001501 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001502 return;
1503 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001504
Michal Vasko48a63ed2016-03-01 09:48:21 +01001505 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001506 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe4300a82017-05-24 10:35:42 +02001507 nc_session_free(ps->sessions[i].session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001508 }
1509 free(ps->sessions);
1510 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001511 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001512 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001513 } else {
1514 for (i = 0; i < ps->session_count; ) {
Michal Vaskoe4300a82017-05-24 10:35:42 +02001515 if (ps->sessions[i].session->status != NC_STATUS_RUNNING) {
1516 session = ps->sessions[i].session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001517 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001518 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001519 continue;
1520 }
1521
1522 ++i;
1523 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001524 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001525
1526 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001527 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001528}
1529
Radek Krejci53691be2016-02-22 13:58:37 +01001530#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001531
Michal Vaskoe2713da2016-08-22 16:06:40 +02001532API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001533nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001534{
Michal Vasko3031aae2016-01-27 16:07:18 +01001535 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001536 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001537
Michal Vasko45e53ae2016-04-07 11:46:03 +02001538 if (!name) {
1539 ERRARG("name");
1540 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001541 }
1542
Michal Vaskoade892d2017-02-22 13:40:35 +01001543 /* BIND LOCK */
1544 pthread_mutex_lock(&server_opts.bind_lock);
1545
1546 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001547 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001548
1549 /* check name uniqueness */
1550 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001551 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001552 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001553 ret = -1;
1554 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001555 }
1556 }
1557
Michal Vasko3031aae2016-01-27 16:07:18 +01001558 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001559 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001560 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001561 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001562 ret = -1;
1563 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001564 }
1565 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001566 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001567
Michal Vaskoe2713da2016-08-22 16:06:40 +02001568 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001569 if (!server_opts.binds) {
1570 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001571 ret = -1;
1572 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001573 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001574
Michal Vasko2e6defd2016-10-07 15:48:15 +02001575 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1576 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1577 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001578 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001579
1580 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001581#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001582 case NC_TI_LIBSSH:
1583 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1584 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1585 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001586 ret = -1;
1587 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001588 }
1589 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1590 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1591 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1592 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1593 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001594#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001595#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001596 case NC_TI_OPENSSL:
1597 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1598 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1599 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001600 ret = -1;
1601 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001602 }
1603 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001604#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001605 default:
1606 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001607 ret = -1;
1608 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001609 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001610
Michal Vaskoade892d2017-02-22 13:40:35 +01001611cleanup:
1612 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001613 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001614
Michal Vaskoade892d2017-02-22 13:40:35 +01001615 /* BIND UNLOCK */
1616 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001617
Michal Vaskoade892d2017-02-22 13:40:35 +01001618 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001619}
1620
Michal Vasko3031aae2016-01-27 16:07:18 +01001621int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001622nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001623{
1624 struct nc_endpt *endpt;
1625 struct nc_bind *bind = NULL;
1626 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001627 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001628
Michal Vasko45e53ae2016-04-07 11:46:03 +02001629 if (!endpt_name) {
1630 ERRARG("endpt_name");
1631 return -1;
1632 } else if ((!address && !port) || (address && port)) {
1633 ERRARG("address and port");
1634 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001635 }
1636
Michal Vaskoe2713da2016-08-22 16:06:40 +02001637 if (address) {
1638 set_addr = 1;
1639 } else {
1640 set_addr = 0;
1641 }
1642
Michal Vaskoade892d2017-02-22 13:40:35 +01001643 /* BIND LOCK */
1644 pthread_mutex_lock(&server_opts.bind_lock);
1645
1646 /* ENDPT LOCK */
1647 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001648 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001649 /* BIND UNLOCK */
1650 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001651 return -1;
1652 }
1653
Michal Vaskoe2713da2016-08-22 16:06:40 +02001654 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001655
Michal Vaskoe2713da2016-08-22 16:06:40 +02001656 if (set_addr) {
1657 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001658 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001659 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001660 }
1661
Michal Vaskoe2713da2016-08-22 16:06:40 +02001662 /* we have all the information we need to create a listening socket */
1663 if (address && port) {
1664 /* create new socket, close the old one */
1665 sock = nc_sock_listen(address, port);
1666 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001667 ret = -1;
1668 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001669 }
1670
1671 if (bind->sock > -1) {
1672 close(bind->sock);
1673 }
1674 bind->sock = sock;
1675 } /* else we are just setting address or port */
1676
1677 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001678 lydict_remove(server_opts.ctx, bind->address);
1679 bind->address = lydict_insert(server_opts.ctx, address, 0);
1680 } else {
1681 bind->port = port;
1682 }
1683
Michal Vaskoe2713da2016-08-22 16:06:40 +02001684 if (sock > -1) {
1685#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001686 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001687#elif defined(NC_ENABLED_SSH)
1688 VRB("Listening on %s:%u for SSH connections.", address, port);
1689#else
1690 VRB("Listening on %s:%u for TLS connections.", address, port);
1691#endif
1692 }
1693
Michal Vasko4c1fb492017-01-30 14:31:07 +01001694cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001695 /* ENDPT UNLOCK */
1696 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001697
Michal Vaskoade892d2017-02-22 13:40:35 +01001698 /* BIND UNLOCK */
1699 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001700
Michal Vasko4c1fb492017-01-30 14:31:07 +01001701 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001702}
1703
Michal Vaskoe2713da2016-08-22 16:06:40 +02001704API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001705nc_server_endpt_set_address(const char *endpt_name, const char *address)
1706{
1707 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1708}
1709
1710API int
1711nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1712{
1713 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1714}
1715
1716API int
Michal Vasko59050372016-11-22 14:33:55 +01001717nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001718{
1719 uint32_t i;
1720 int ret = -1;
1721
Michal Vaskoade892d2017-02-22 13:40:35 +01001722 /* BIND LOCK */
1723 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001724
Michal Vaskoade892d2017-02-22 13:40:35 +01001725 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001726 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001727
Michal Vasko59050372016-11-22 14:33:55 +01001728 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001729 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001730 for (i = 0; i < server_opts.endpt_count; ++i) {
1731 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001732 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001733#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001734 case NC_TI_LIBSSH:
1735 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1736 free(server_opts.endpts[i].opts.ssh);
1737 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001738#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001739#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001740 case NC_TI_OPENSSL:
1741 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1742 free(server_opts.endpts[i].opts.tls);
1743 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001744#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001745 default:
1746 ERRINT;
1747 /* won't get here ...*/
1748 break;
1749 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001750 ret = 0;
1751 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001752 free(server_opts.endpts);
1753 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001754
1755 /* remove all binds */
1756 for (i = 0; i < server_opts.endpt_count; ++i) {
1757 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1758 if (server_opts.binds[i].sock > -1) {
1759 close(server_opts.binds[i].sock);
1760 }
1761 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001762 free(server_opts.binds);
1763 server_opts.binds = NULL;
1764
Michal Vasko3031aae2016-01-27 16:07:18 +01001765 server_opts.endpt_count = 0;
1766
Michal Vasko1a38c862016-01-15 15:50:07 +01001767 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001768 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001769 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001770 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001771 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001772 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001773 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001774#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001775 case NC_TI_LIBSSH:
1776 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1777 free(server_opts.endpts[i].opts.ssh);
1778 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001779#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001780#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001781 case NC_TI_OPENSSL:
1782 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1783 free(server_opts.endpts[i].opts.tls);
1784 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001785#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001786 default:
1787 ERRINT;
1788 break;
1789 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001790
Michal Vaskoe2713da2016-08-22 16:06:40 +02001791 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001792 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1793 if (server_opts.binds[i].sock > -1) {
1794 close(server_opts.binds[i].sock);
1795 }
1796
1797 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001798 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001799 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001800 free(server_opts.binds);
1801 server_opts.binds = NULL;
1802 free(server_opts.endpts);
1803 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001804 } else if (i < server_opts.endpt_count) {
1805 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1806 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001807 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001808
1809 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001810 if (name) {
1811 break;
1812 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001813 }
1814 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001815 }
1816
Michal Vaskoade892d2017-02-22 13:40:35 +01001817 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001818 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001819
Michal Vaskoade892d2017-02-22 13:40:35 +01001820 /* BIND UNLOCK */
1821 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001822
1823 return ret;
1824}
1825
Michal Vasko71090fc2016-05-24 16:37:28 +02001826API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001827nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001828{
Michal Vasko71090fc2016-05-24 16:37:28 +02001829 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001830 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001831 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001832 uint16_t port, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001833
Michal Vasko45e53ae2016-04-07 11:46:03 +02001834 if (!server_opts.ctx) {
1835 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001836 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001837 } else if (!session) {
1838 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001839 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001840 }
1841
Michal Vaskoade892d2017-02-22 13:40:35 +01001842 /* BIND LOCK */
1843 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001844
1845 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001846 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01001847 /* BIND UNLOCK */
1848 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001849 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001850 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001851
Michal Vaskoe2713da2016-08-22 16:06:40 +02001852 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001853 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001854 /* BIND UNLOCK */
1855 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001856 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001857 if (!ret) {
1858 return NC_MSG_WOULDBLOCK;
1859 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001860 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001861 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001862
1863 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
1864 /* ENDPT READ LOCK */
1865 pthread_rwlock_rdlock(&server_opts.endpt_lock);
1866
1867 /* BIND UNLOCK */
1868 pthread_mutex_unlock(&server_opts.bind_lock);
1869
Michal Vaskob48aa812016-01-18 14:13:09 +01001870 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001871
Michal Vaskoade892d2017-02-22 13:40:35 +01001872 *session = nc_new_session(0);
Michal Vasko686aa312016-01-21 15:58:18 +01001873 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001874 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001875 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001876 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001877 msgtype = NC_MSG_ERROR;
1878 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001879 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001880 (*session)->status = NC_STATUS_STARTING;
1881 (*session)->side = NC_SERVER;
1882 (*session)->ctx = server_opts.ctx;
1883 (*session)->flags = NC_SESSION_SHAREDCTX;
1884 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1885 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001886
1887 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001888 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001889 pthread_cond_init((*session)->ti_cond, NULL);
1890 *(*session)->ti_inuse = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001891
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001892 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001893#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001894 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1895 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001896 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001897 if (ret < 0) {
1898 msgtype = NC_MSG_ERROR;
1899 goto cleanup;
1900 } else if (!ret) {
1901 msgtype = NC_MSG_WOULDBLOCK;
1902 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001903 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001904 } else
1905#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001906#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001907 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1908 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001909 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001910 if (ret < 0) {
1911 msgtype = NC_MSG_ERROR;
1912 goto cleanup;
1913 } else if (!ret) {
1914 msgtype = NC_MSG_WOULDBLOCK;
1915 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001916 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001917 } else
1918#endif
1919 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001920 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001921 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001922 msgtype = NC_MSG_ERROR;
1923 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001924 }
1925
Michal Vasko2cc4c682016-03-01 09:16:48 +01001926 (*session)->data = NULL;
1927
Michal Vaskoade892d2017-02-22 13:40:35 +01001928 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001929 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001930
Michal Vaskob48aa812016-01-18 14:13:09 +01001931 /* assign new SID atomically */
1932 /* LOCK */
1933 pthread_spin_lock(&server_opts.sid_lock);
1934 (*session)->id = server_opts.new_session_id++;
1935 /* UNLOCK */
1936 pthread_spin_unlock(&server_opts.sid_lock);
1937
Michal Vasko9e036d52016-01-08 10:49:26 +01001938 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001939 msgtype = nc_handshake(*session);
1940 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001941 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001942 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001943 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001944 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001945 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001946 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001947
Michal Vasko71090fc2016-05-24 16:37:28 +02001948 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001949
Michal Vasko71090fc2016-05-24 16:37:28 +02001950cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001951 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001952 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001953
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001954 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001955 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001956 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001957}
1958
Michal Vasko2e6defd2016-10-07 15:48:15 +02001959API int
1960nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1961{
1962 uint16_t i;
1963
1964 if (!name) {
1965 ERRARG("name");
1966 return -1;
1967 } else if (!ti) {
1968 ERRARG("ti");
1969 return -1;
1970 }
1971
1972 /* WRITE LOCK */
1973 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1974
1975 /* check name uniqueness */
1976 for (i = 0; i < server_opts.ch_client_count; ++i) {
1977 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1978 ERR("Call Home client \"%s\" already exists.", name);
1979 /* WRITE UNLOCK */
1980 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1981 return -1;
1982 }
1983 }
1984
1985 ++server_opts.ch_client_count;
1986 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
1987 if (!server_opts.ch_clients) {
1988 ERRMEM;
1989 /* WRITE UNLOCK */
1990 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1991 return -1;
1992 }
1993 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1994 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02001995 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
1996 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001997
1998 switch (ti) {
1999#ifdef NC_ENABLED_SSH
2000 case NC_TI_LIBSSH:
2001 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2002 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
2003 ERRMEM;
2004 /* WRITE UNLOCK */
2005 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2006 return -1;
2007 }
2008 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
2009 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2010 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
2011 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
2012 break;
2013#endif
2014#ifdef NC_ENABLED_TLS
2015 case NC_TI_OPENSSL:
2016 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2017 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
2018 ERRMEM;
2019 /* WRITE UNLOCK */
2020 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2021 return -1;
2022 }
2023 break;
2024#endif
2025 default:
2026 ERRINT;
2027 /* WRITE UNLOCK */
2028 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2029 return -1;
2030 }
2031
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002032 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
2033
Michal Vasko2e6defd2016-10-07 15:48:15 +02002034 /* set CH default options */
2035 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
2036 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
2037
2038 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
2039
2040 /* WRITE UNLOCK */
2041 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2042
2043 return 0;
2044}
2045
2046API int
Michal Vasko59050372016-11-22 14:33:55 +01002047nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002048{
2049 uint16_t i, j;
2050 int ret = -1;
2051
2052 /* WRITE LOCK */
2053 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2054
Michal Vasko59050372016-11-22 14:33:55 +01002055 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002056 /* remove all CH clients */
2057 for (i = 0; i < server_opts.ch_client_count; ++i) {
2058 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2059
2060 /* remove all endpoints */
2061 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2062 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2063 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2064 }
2065 free(server_opts.ch_clients[i].ch_endpts);
2066
2067 switch (server_opts.ch_clients[i].ti) {
2068#ifdef NC_ENABLED_SSH
2069 case NC_TI_LIBSSH:
2070 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2071 free(server_opts.ch_clients[i].opts.ssh);
2072 break;
2073#endif
2074#ifdef NC_ENABLED_TLS
2075 case NC_TI_OPENSSL:
2076 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2077 free(server_opts.ch_clients[i].opts.tls);
2078 break;
2079#endif
2080 default:
2081 ERRINT;
2082 /* won't get here ...*/
2083 break;
2084 }
2085
2086 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2087
2088 ret = 0;
2089 }
2090 free(server_opts.ch_clients);
2091 server_opts.ch_clients = NULL;
2092
2093 server_opts.ch_client_count = 0;
2094
2095 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002096 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002097 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002098 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 +02002099 /* remove endpt */
2100 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2101
2102 switch (server_opts.ch_clients[i].ti) {
2103#ifdef NC_ENABLED_SSH
2104 case NC_TI_LIBSSH:
2105 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2106 free(server_opts.ch_clients[i].opts.ssh);
2107 break;
2108#endif
2109#ifdef NC_ENABLED_TLS
2110 case NC_TI_OPENSSL:
2111 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2112 free(server_opts.ch_clients[i].opts.tls);
2113 break;
2114#endif
2115 default:
2116 ERRINT;
2117 break;
2118 }
2119
2120 /* remove all endpoints */
2121 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2122 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2123 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2124 }
2125 free(server_opts.ch_clients[i].ch_endpts);
2126
2127 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2128
2129 /* move last client and endpoint(s) to the empty space */
2130 --server_opts.ch_client_count;
2131 if (i < server_opts.ch_client_count) {
2132 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2133 sizeof *server_opts.ch_clients);
2134 } else if (!server_opts.ch_client_count) {
2135 free(server_opts.ch_clients);
2136 server_opts.ch_clients = NULL;
2137 }
2138
2139 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002140 if (name) {
2141 break;
2142 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002143 }
2144 }
2145 }
2146
2147 /* WRITE UNLOCK */
2148 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2149
2150 return ret;
2151}
2152
2153API int
2154nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2155{
2156 uint16_t i;
2157 struct nc_ch_client *client;
2158
2159 if (!client_name) {
2160 ERRARG("client_name");
2161 return -1;
2162 } else if (!endpt_name) {
2163 ERRARG("endpt_name");
2164 return -1;
2165 }
2166
2167 /* LOCK */
2168 client = nc_server_ch_client_lock(client_name, 0, NULL);
2169 if (!client) {
2170 return -1;
2171 }
2172
2173 for (i = 0; i < client->ch_endpt_count; ++i) {
2174 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2175 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2176 /* UNLOCK */
2177 nc_server_ch_client_unlock(client);
2178 return -1;
2179 }
2180 }
2181
2182 ++client->ch_endpt_count;
2183 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2184 if (!client->ch_endpts) {
2185 ERRMEM;
2186 /* UNLOCK */
2187 nc_server_ch_client_unlock(client);
2188 return -1;
2189 }
2190
2191 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2192 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2193 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
2194
2195 /* UNLOCK */
2196 nc_server_ch_client_unlock(client);
2197
2198 return 0;
2199}
2200
2201API int
2202nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2203{
2204 uint16_t i;
2205 int ret = -1;
2206 struct nc_ch_client *client;
2207
2208 if (!client_name) {
2209 ERRARG("client_name");
2210 return -1;
2211 }
2212
2213 /* LOCK */
2214 client = nc_server_ch_client_lock(client_name, 0, NULL);
2215 if (!client) {
2216 return -1;
2217 }
2218
2219 if (!endpt_name) {
2220 /* remove all endpoints */
2221 for (i = 0; i < client->ch_endpt_count; ++i) {
2222 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2223 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2224 }
2225 free(client->ch_endpts);
2226 client->ch_endpts = NULL;
2227 client->ch_endpt_count = 0;
2228
2229 ret = 0;
2230 } else {
2231 for (i = 0; i < client->ch_endpt_count; ++i) {
2232 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2233 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2234 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002235
Michal Vasko4f921012016-10-20 14:07:45 +02002236 /* move last endpoint to the empty space */
2237 --client->ch_endpt_count;
2238 if (i < client->ch_endpt_count) {
2239 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2240 } else if (!server_opts.ch_client_count) {
2241 free(server_opts.ch_clients);
2242 server_opts.ch_clients = NULL;
2243 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002244
Michal Vasko4f921012016-10-20 14:07:45 +02002245 ret = 0;
2246 break;
2247 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002248 }
2249 }
2250
2251 /* UNLOCK */
2252 nc_server_ch_client_unlock(client);
2253
2254 return ret;
2255}
2256
2257API int
2258nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2259{
2260 uint16_t i;
2261 int ret = -1;
2262 struct nc_ch_client *client;
2263
2264 if (!client_name) {
2265 ERRARG("client_name");
2266 return -1;
2267 } else if (!endpt_name) {
2268 ERRARG("endpt_name");
2269 return -1;
2270 } else if (!address) {
2271 ERRARG("address");
2272 return -1;
2273 }
2274
2275 /* LOCK */
2276 client = nc_server_ch_client_lock(client_name, 0, NULL);
2277 if (!client) {
2278 return -1;
2279 }
2280
2281 for (i = 0; i < client->ch_endpt_count; ++i) {
2282 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2283 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2284 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2285
2286 ret = 0;
2287 break;
2288 }
2289 }
2290
2291 /* UNLOCK */
2292 nc_server_ch_client_unlock(client);
2293
2294 if (ret == -1) {
2295 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2296 }
2297
2298 return ret;
2299}
2300
2301API int
2302nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2303{
2304 uint16_t i;
2305 int ret = -1;
2306 struct nc_ch_client *client;
2307
2308 if (!client_name) {
2309 ERRARG("client_name");
2310 return -1;
2311 } else if (!endpt_name) {
2312 ERRARG("endpt_name");
2313 return -1;
2314 } else if (!port) {
2315 ERRARG("port");
2316 return -1;
2317 }
2318
2319 /* LOCK */
2320 client = nc_server_ch_client_lock(client_name, 0, NULL);
2321 if (!client) {
2322 return -1;
2323 }
2324
2325 for (i = 0; i < client->ch_endpt_count; ++i) {
2326 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2327 client->ch_endpts[i].port = port;
2328
2329 ret = 0;
2330 break;
2331 }
2332 }
2333
2334 /* UNLOCK */
2335 nc_server_ch_client_unlock(client);
2336
2337 if (ret == -1) {
2338 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2339 }
2340
2341 return ret;
2342}
2343
2344API int
2345nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2346{
2347 struct nc_ch_client *client;
2348
2349 if (!client_name) {
2350 ERRARG("client_name");
2351 return -1;
2352 } else if (!conn_type) {
2353 ERRARG("conn_type");
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 if (client->conn_type != conn_type) {
2364 client->conn_type = conn_type;
2365
2366 /* set default options */
2367 switch (conn_type) {
2368 case NC_CH_PERSIST:
2369 client->conn.persist.idle_timeout = 86400;
2370 client->conn.persist.ka_max_wait = 30;
2371 client->conn.persist.ka_max_attempts = 3;
2372 break;
2373 case NC_CH_PERIOD:
2374 client->conn.period.idle_timeout = 300;
2375 client->conn.period.reconnect_timeout = 60;
2376 break;
2377 default:
2378 ERRINT;
2379 break;
2380 }
2381 }
2382
2383 /* UNLOCK */
2384 nc_server_ch_client_unlock(client);
2385
2386 return 0;
2387}
2388
2389API int
2390nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2391{
2392 struct nc_ch_client *client;
2393
2394 if (!client_name) {
2395 ERRARG("client_name");
2396 return -1;
2397 }
2398
2399 /* LOCK */
2400 client = nc_server_ch_client_lock(client_name, 0, NULL);
2401 if (!client) {
2402 return -1;
2403 }
2404
2405 if (client->conn_type != NC_CH_PERSIST) {
2406 ERR("Call Home client \"%s\" is not of persistent connection type.");
2407 /* UNLOCK */
2408 nc_server_ch_client_unlock(client);
2409 return -1;
2410 }
2411
2412 client->conn.persist.idle_timeout = idle_timeout;
2413
2414 /* UNLOCK */
2415 nc_server_ch_client_unlock(client);
2416
2417 return 0;
2418}
2419
2420API int
2421nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2422{
2423 struct nc_ch_client *client;
2424
2425 if (!client_name) {
2426 ERRARG("client_name");
2427 return -1;
2428 } else if (!max_wait) {
2429 ERRARG("max_wait");
2430 return -1;
2431 }
2432
2433 /* LOCK */
2434 client = nc_server_ch_client_lock(client_name, 0, NULL);
2435 if (!client) {
2436 return -1;
2437 }
2438
2439 if (client->conn_type != NC_CH_PERSIST) {
2440 ERR("Call Home client \"%s\" is not of persistent connection type.");
2441 /* UNLOCK */
2442 nc_server_ch_client_unlock(client);
2443 return -1;
2444 }
2445
2446 client->conn.persist.ka_max_wait = max_wait;
2447
2448 /* UNLOCK */
2449 nc_server_ch_client_unlock(client);
2450
2451 return 0;
2452}
2453
2454API int
2455nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2456{
2457 struct nc_ch_client *client;
2458
2459 if (!client_name) {
2460 ERRARG("client_name");
2461 return -1;
2462 }
2463
2464 /* LOCK */
2465 client = nc_server_ch_client_lock(client_name, 0, NULL);
2466 if (!client) {
2467 return -1;
2468 }
2469
2470 if (client->conn_type != NC_CH_PERSIST) {
2471 ERR("Call Home client \"%s\" is not of persistent connection type.");
2472 /* UNLOCK */
2473 nc_server_ch_client_unlock(client);
2474 return -1;
2475 }
2476
2477 client->conn.persist.ka_max_attempts = max_attempts;
2478
2479 /* UNLOCK */
2480 nc_server_ch_client_unlock(client);
2481
2482 return 0;
2483}
2484
2485API int
2486nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2487{
2488 struct nc_ch_client *client;
2489
2490 if (!client_name) {
2491 ERRARG("client_name");
2492 return -1;
2493 }
2494
2495 /* LOCK */
2496 client = nc_server_ch_client_lock(client_name, 0, NULL);
2497 if (!client) {
2498 return -1;
2499 }
2500
2501 if (client->conn_type != NC_CH_PERIOD) {
2502 ERR("Call Home client \"%s\" is not of periodic connection type.");
2503 /* UNLOCK */
2504 nc_server_ch_client_unlock(client);
2505 return -1;
2506 }
2507
2508 client->conn.period.idle_timeout = idle_timeout;
2509
2510 /* UNLOCK */
2511 nc_server_ch_client_unlock(client);
2512
2513 return 0;
2514}
2515
2516API int
2517nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2518{
2519 struct nc_ch_client *client;
2520
2521 if (!client_name) {
2522 ERRARG("client_name");
2523 return -1;
2524 } else if (!reconnect_timeout) {
2525 ERRARG("reconnect_timeout");
2526 return -1;
2527 }
2528
2529 /* LOCK */
2530 client = nc_server_ch_client_lock(client_name, 0, NULL);
2531 if (!client) {
2532 return -1;
2533 }
2534
2535 if (client->conn_type != NC_CH_PERIOD) {
2536 ERR("Call Home client \"%s\" is not of periodic connection type.");
2537 /* UNLOCK */
2538 nc_server_ch_client_unlock(client);
2539 return -1;
2540 }
2541
2542 client->conn.period.reconnect_timeout = reconnect_timeout;
2543
2544 /* UNLOCK */
2545 nc_server_ch_client_unlock(client);
2546
2547 return 0;
2548}
2549
2550API int
2551nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2552{
2553 struct nc_ch_client *client;
2554
2555 if (!client_name) {
2556 ERRARG("client_name");
2557 return -1;
2558 }
2559
2560 /* LOCK */
2561 client = nc_server_ch_client_lock(client_name, 0, NULL);
2562 if (!client) {
2563 return -1;
2564 }
2565
2566 client->start_with = start_with;
2567
2568 /* UNLOCK */
2569 nc_server_ch_client_unlock(client);
2570
2571 return 0;
2572}
2573
2574API int
2575nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2576{
2577 struct nc_ch_client *client;
2578
2579 if (!client_name) {
2580 ERRARG("client_name");
2581 return -1;
2582 } else if (!max_attempts) {
2583 ERRARG("max_attempts");
2584 return -1;
2585 }
2586
2587 /* LOCK */
2588 client = nc_server_ch_client_lock(client_name, 0, NULL);
2589 if (!client) {
2590 return -1;
2591 }
2592
2593 client->max_attempts = max_attempts;
2594
2595 /* UNLOCK */
2596 nc_server_ch_client_unlock(client);
2597
2598 return 0;
2599}
2600
2601/* client lock is expected to be held */
2602static NC_MSG_TYPE
2603nc_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 +01002604{
Michal Vasko71090fc2016-05-24 16:37:28 +02002605 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002606 int sock, ret;
2607
Michal Vasko2e6defd2016-10-07 15:48:15 +02002608 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002609 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002610 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002611 }
2612
Michal Vaskoade892d2017-02-22 13:40:35 +01002613 *session = nc_new_session(0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002614 if (!(*session)) {
2615 ERRMEM;
2616 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002617 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002618 }
2619 (*session)->status = NC_STATUS_STARTING;
2620 (*session)->side = NC_SERVER;
2621 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002622 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002623 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2624 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002625
2626 /* transport lock */
Michal Vaskob05053d2016-01-22 16:12:06 +01002627 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01002628 pthread_cond_init((*session)->ti_cond, NULL);
2629 *(*session)->ti_inuse = 0;
Michal Vaskob05053d2016-01-22 16:12:06 +01002630
2631 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002632#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002633 if (client->ti == NC_TI_LIBSSH) {
2634 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002635 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002636 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002637
Michal Vasko71090fc2016-05-24 16:37:28 +02002638 if (ret < 0) {
2639 msgtype = NC_MSG_ERROR;
2640 goto fail;
2641 } else if (!ret) {
2642 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002643 goto fail;
2644 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002645 } else
2646#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002647#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002648 if (client->ti == NC_TI_OPENSSL) {
2649 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002650 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002651 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002652
Michal Vasko71090fc2016-05-24 16:37:28 +02002653 if (ret < 0) {
2654 msgtype = NC_MSG_ERROR;
2655 goto fail;
2656 } else if (!ret) {
2657 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002658 goto fail;
2659 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002660 } else
2661#endif
2662 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002663 ERRINT;
2664 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002665 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002666 goto fail;
2667 }
2668
2669 /* assign new SID atomically */
2670 /* LOCK */
2671 pthread_spin_lock(&server_opts.sid_lock);
2672 (*session)->id = server_opts.new_session_id++;
2673 /* UNLOCK */
2674 pthread_spin_unlock(&server_opts.sid_lock);
2675
2676 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002677 msgtype = nc_handshake(*session);
2678 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002679 goto fail;
2680 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002681 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002682 (*session)->status = NC_STATUS_RUNNING;
2683
Michal Vasko71090fc2016-05-24 16:37:28 +02002684 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002685
2686fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002687 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002688 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002689 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002690}
2691
Michal Vasko2e6defd2016-10-07 15:48:15 +02002692struct nc_ch_client_thread_arg {
2693 char *client_name;
2694 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2695};
2696
2697static struct nc_ch_client *
2698nc_server_ch_client_with_endpt_lock(const char *name)
2699{
2700 struct nc_ch_client *client;
2701
2702 while (1) {
2703 /* LOCK */
2704 client = nc_server_ch_client_lock(name, 0, NULL);
2705 if (!client) {
2706 return NULL;
2707 }
2708 if (client->ch_endpt_count) {
2709 return client;
2710 }
2711 /* no endpoints defined yet */
2712
2713 /* UNLOCK */
2714 nc_server_ch_client_unlock(client);
2715
2716 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2717 }
2718
2719 return NULL;
2720}
2721
2722static int
2723nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2724{
2725 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002726 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002727 struct timespec ts;
2728 struct nc_ch_client *client;
2729
2730 /* session created, initialize condition */
2731 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2732 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2733 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2734 ERRMEM;
2735 nc_session_free(session, NULL);
2736 return -1;
2737 }
2738 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2739 pthread_cond_init(session->opts.server.ch_cond, NULL);
2740
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002741 session->flags |= NC_SESSION_CALLHOME;
2742
Michal Vasko2e6defd2016-10-07 15:48:15 +02002743 /* CH LOCK */
2744 pthread_mutex_lock(session->opts.server.ch_lock);
2745
2746 /* give the session to the user */
2747 data->session_clb(data->client_name, session);
2748
2749 do {
2750 nc_gettimespec(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01002751 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002752
2753 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2754 if (ret && (ret != ETIMEDOUT)) {
2755 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2756 goto ch_client_remove;
2757 }
2758
2759 /* check whether the client was not removed */
2760 /* LOCK */
2761 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2762 if (!client) {
2763 /* client was removed, finish thread */
2764 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2765 data->client_name);
2766 goto ch_client_remove;
2767 }
2768
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002769 if (client->conn_type == NC_CH_PERSIST) {
2770 /* TODO keep-alives */
2771 idle_timeout = client->conn.persist.idle_timeout;
2772 } else {
2773 idle_timeout = client->conn.period.idle_timeout;
2774 }
2775
Michal Vasko3486a7c2017-03-03 13:28:07 +01002776 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 +02002777 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2778 session->status = NC_STATUS_INVALID;
2779 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2780 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002781
2782 /* UNLOCK */
2783 nc_server_ch_client_unlock(client);
2784
2785 } while (session->status == NC_STATUS_RUNNING);
2786
2787 /* CH UNLOCK */
2788 pthread_mutex_unlock(session->opts.server.ch_lock);
2789
2790 return 0;
2791
2792ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002793 /* make the session a standard one */
2794 pthread_cond_destroy(session->opts.server.ch_cond);
2795 free(session->opts.server.ch_cond);
2796 session->opts.server.ch_cond = NULL;
2797
2798 session->flags &= ~NC_SESSION_CALLHOME;
2799
Michal Vasko2e6defd2016-10-07 15:48:15 +02002800 /* CH UNLOCK */
2801 pthread_mutex_unlock(session->opts.server.ch_lock);
2802
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002803 pthread_mutex_destroy(session->opts.server.ch_lock);
2804 free(session->opts.server.ch_lock);
2805 session->opts.server.ch_lock = NULL;
2806
Michal Vasko2e6defd2016-10-07 15:48:15 +02002807 return 1;
2808}
2809
2810static void *
2811nc_ch_client_thread(void *arg)
2812{
2813 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2814 NC_MSG_TYPE msgtype;
2815 uint8_t cur_attempts = 0;
2816 uint16_t i;
Michal Vasko9550cf12017-03-21 15:33:58 +01002817 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002818 struct nc_ch_endpt *cur_endpt;
2819 struct nc_session *session;
2820 struct nc_ch_client *client;
2821
2822 /* LOCK */
2823 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2824 if (!client) {
2825 goto cleanup;
2826 }
2827
2828 cur_endpt = &client->ch_endpts[0];
2829 cur_endpt_name = strdup(cur_endpt->name);
2830
Michal Vasko29af44b2016-10-13 10:59:55 +02002831 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002832 while (1) {
2833 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2834
2835 if (msgtype == NC_MSG_HELLO) {
2836 /* UNLOCK */
2837 nc_server_ch_client_unlock(client);
2838
Michal Vasko29af44b2016-10-13 10:59:55 +02002839 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002840 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2841 goto cleanup;
2842 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002843 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002844
2845 /* LOCK */
2846 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2847 if (!client) {
2848 goto cleanup;
2849 }
2850
2851 /* session changed status -> it was disconnected for whatever reason,
2852 * persistent connection immediately tries to reconnect, periodic waits some first */
2853 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002854 /* UNLOCK */
2855 nc_server_ch_client_unlock(client);
2856
2857 /* TODO wake up sometimes to check for new notifications */
2858 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2859
2860 /* LOCK */
2861 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2862 if (!client) {
2863 goto cleanup;
2864 }
2865 }
2866
2867 /* set next endpoint to try */
2868 if (client->start_with == NC_CH_FIRST_LISTED) {
2869 cur_endpt = &client->ch_endpts[0];
2870 free(cur_endpt_name);
2871 cur_endpt_name = strdup(cur_endpt->name);
2872 } /* else we keep the current one */
2873 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02002874 /* UNLOCK */
2875 nc_server_ch_client_unlock(client);
2876
Michal Vasko2e6defd2016-10-07 15:48:15 +02002877 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002878 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2879
Michal Vasko6bb116b2016-10-26 13:53:46 +02002880 /* LOCK */
2881 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2882 if (!client) {
2883 goto cleanup;
2884 }
2885
Michal Vasko2e6defd2016-10-07 15:48:15 +02002886 ++cur_attempts;
2887 if (cur_attempts == client->max_attempts) {
2888 for (i = 0; i < client->ch_endpt_count; ++i) {
2889 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2890 break;
2891 }
2892 }
2893 if (i < client->ch_endpt_count - 1) {
2894 /* just go to the next endpoint */
2895 cur_endpt = &client->ch_endpts[i + 1];
2896 free(cur_endpt_name);
2897 cur_endpt_name = strdup(cur_endpt->name);
2898 } else {
2899 /* cur_endpoint was removed or is the last, either way start with the first one */
2900 cur_endpt = &client->ch_endpts[0];
2901 free(cur_endpt_name);
2902 cur_endpt_name = strdup(cur_endpt->name);
2903 }
2904
2905 cur_attempts = 0;
2906 } /* else we keep the current one */
2907 }
2908 }
2909
2910cleanup:
2911 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01002912 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002913 free(data->client_name);
2914 free(data);
2915 return NULL;
2916}
2917
2918API int
2919nc_connect_ch_client_dispatch(const char *client_name,
2920 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2921 int ret;
2922 pthread_t tid;
2923 struct nc_ch_client_thread_arg *arg;
2924
2925 if (!client_name) {
2926 ERRARG("client_name");
2927 return -1;
2928 } else if (!session_clb) {
2929 ERRARG("session_clb");
2930 return -1;
2931 }
2932
2933 arg = malloc(sizeof *arg);
2934 if (!arg) {
2935 ERRMEM;
2936 return -1;
2937 }
2938 arg->client_name = strdup(client_name);
2939 if (!arg->client_name) {
2940 ERRMEM;
2941 free(arg);
2942 return -1;
2943 }
2944 arg->session_clb = session_clb;
2945
2946 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2947 if (ret) {
2948 ERR("Creating a new thread failed (%s).", strerror(ret));
2949 free(arg->client_name);
2950 free(arg);
2951 return -1;
2952 }
2953 /* the thread now manages arg */
2954
2955 pthread_detach(tid);
2956
2957 return 0;
2958}
2959
Radek Krejci53691be2016-02-22 13:58:37 +01002960#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002961
Michal Vaskoe8e07702017-03-15 10:19:30 +01002962API int
2963nc_server_endpt_count(void)
2964{
2965 return server_opts.endpt_count;
2966}
2967
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002968API time_t
2969nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02002970{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002971 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02002972 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002973 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02002974 }
2975
Michal Vasko2e6defd2016-10-07 15:48:15 +02002976 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02002977}
Michal Vasko3486a7c2017-03-03 13:28:07 +01002978
2979API void
2980nc_session_set_notif_status(struct nc_session *session, int notif_status)
2981{
2982 if (!session || (session->side != NC_SERVER)) {
2983 ERRARG("session");
2984 return;
2985 }
2986
2987 session->opts.server.ntf_status = (notif_status ? 1 : 0);
2988}
2989
2990API int
2991nc_session_get_notif_status(const struct nc_session *session)
2992{
2993 if (!session || (session->side != NC_SERVER)) {
2994 ERRARG("session");
2995 return 0;
2996 }
2997
2998 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01002999}