blob: 13ea3edf3793a541ba8807c632c580e9d187e76a [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 server session manipulation functions
5 *
Michal Vasko18aeb5d2017-02-17 09:23:56 +01006 * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01007 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
Miroslav Mareš9563b812017-08-19 17:45:36 +020014#define _GNU_SOURCE /* signals, threads */
Michal Vasko086311b2016-01-08 09:53:11 +010015
16#include <stdint.h>
17#include <stdlib.h>
18#include <errno.h>
19#include <string.h>
20#include <poll.h>
21#include <sys/types.h>
22#include <sys/socket.h>
23#include <netinet/in.h>
24#include <arpa/inet.h>
25#include <unistd.h>
Michal Vasko0190bc32016-03-02 15:47:49 +010026#include <fcntl.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010027#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010028#include <time.h>
Michal Vaskoade892d2017-02-22 13:40:35 +010029#include <signal.h>
Michal Vasko086311b2016-01-08 09:53:11 +010030
Michal Vasko1a38c862016-01-15 15:50:07 +010031#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010032#include "session_server.h"
33
Michal Vaskob48aa812016-01-18 14:13:09 +010034struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010035#ifdef NC_ENABLED_SSH
36 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
37#endif
38 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020039 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
40 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010041};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010042
fanchanghu966f2de2016-07-21 02:28:57 -040043static nc_rpc_clb global_rpc_clb = NULL;
44
Michal Vasko3031aae2016-01-27 16:07:18 +010045struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010046nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010047{
48 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010049 struct nc_endpt *endpt = NULL;
50
Michal Vaskoade892d2017-02-22 13:40:35 +010051 /* WRITE LOCK */
52 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010053
54 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020055 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010056 endpt = &server_opts.endpts[i];
57 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010058 }
59 }
60
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010061 if (!endpt) {
62 ERR("Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010063 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020064 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010065 return NULL;
66 }
67
Michal Vaskoe2713da2016-08-22 16:06:40 +020068 if (idx) {
69 *idx = i;
70 }
71
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010072 return endpt;
73}
74
Michal Vasko2e6defd2016-10-07 15:48:15 +020075struct nc_ch_client *
76nc_server_ch_client_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010077{
Michal Vasko2e6defd2016-10-07 15:48:15 +020078 uint16_t i;
79 struct nc_ch_client *client = NULL;
80
81 /* READ LOCK */
82 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
83
84 for (i = 0; i < server_opts.ch_client_count; ++i) {
85 if (!strcmp(server_opts.ch_clients[i].name, name) && (!ti || (server_opts.ch_clients[i].ti == ti))) {
86 client = &server_opts.ch_clients[i];
87 break;
88 }
89 }
90
91 if (!client) {
92 ERR("Call Home client \"%s\" was not found.", name);
93 /* READ UNLOCK */
94 pthread_rwlock_unlock(&server_opts.ch_client_lock);
95 return NULL;
96 }
97
98 /* CH CLIENT LOCK */
99 pthread_mutex_lock(&client->lock);
100
101 if (idx) {
102 *idx = i;
103 }
104
105 return client;
106}
107
108void
109nc_server_ch_client_unlock(struct nc_ch_client *client)
110{
111 /* CH CLIENT UNLOCK */
112 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100113
114 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200115 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100116}
Michal Vasko086311b2016-01-08 09:53:11 +0100117
Michal Vasko1a38c862016-01-15 15:50:07 +0100118API void
119nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
120{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200121 if (!session) {
122 ERRARG("session");
123 return;
124 } else if (!reason) {
125 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100126 return;
127 }
128
Michal Vasko142cfea2017-08-07 10:12:11 +0200129 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
130 session->killed_by = 0;
131 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100132 session->term_reason = reason;
133}
134
Michal Vasko142cfea2017-08-07 10:12:11 +0200135API void
136nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
137{
138 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
139 ERRARG("session");
140 return;
141 } else if (!sid) {
142 ERRARG("sid");
143 return;
144 }
145
146 session->killed_by = sid;
147}
148
149API void
150nc_session_set_status(struct nc_session *session, NC_STATUS status)
151{
152 if (!session) {
153 ERRARG("session");
154 return;
155 } else if (!status) {
156 ERRARG("status");
157 return;
158 }
159
160 session->status = status;
161}
162
Michal Vasko086311b2016-01-08 09:53:11 +0100163int
Michal Vaskof05562c2016-01-20 12:06:43 +0100164nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100165{
166 const int optVal = 1;
167 const socklen_t optLen = sizeof(optVal);
168 int is_ipv4, sock;
169 struct sockaddr_storage saddr;
170
171 struct sockaddr_in *saddr4;
172 struct sockaddr_in6 *saddr6;
173
174
175 if (!strchr(address, ':')) {
176 is_ipv4 = 1;
177 } else {
178 is_ipv4 = 0;
179 }
180
181 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
182 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100183 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100184 goto fail;
185 }
186
187 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100188 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100189 goto fail;
190 }
191
192 bzero(&saddr, sizeof(struct sockaddr_storage));
193 if (is_ipv4) {
194 saddr4 = (struct sockaddr_in *)&saddr;
195
196 saddr4->sin_family = AF_INET;
197 saddr4->sin_port = htons(port);
198
199 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100200 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100201 goto fail;
202 }
203
204 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100205 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100206 goto fail;
207 }
208
209 } else {
210 saddr6 = (struct sockaddr_in6 *)&saddr;
211
212 saddr6->sin6_family = AF_INET6;
213 saddr6->sin6_port = htons(port);
214
215 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100216 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100217 goto fail;
218 }
219
220 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100221 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100222 goto fail;
223 }
224 }
225
Michal Vaskofb89d772016-01-08 12:25:35 +0100226 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100227 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100228 goto fail;
229 }
230
231 return sock;
232
233fail:
234 if (sock > -1) {
235 close(sock);
236 }
237
238 return -1;
239}
240
241int
Michal Vasko3031aae2016-01-27 16:07:18 +0100242nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx)
Michal Vasko086311b2016-01-08 09:53:11 +0100243{
Michal Vaskof54cd352017-02-22 13:42:02 +0100244 sigset_t sigmask, origmask;
Michal Vaskoac2f6182017-01-30 14:32:03 +0100245 uint16_t i, j, pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100246 struct pollfd *pfd;
247 struct sockaddr_storage saddr;
248 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100249 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100250
251 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100252 if (!pfd) {
253 ERRMEM;
254 return -1;
255 }
256
Michal Vaskoac2f6182017-01-30 14:32:03 +0100257 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200258 if (binds[i].sock < 0) {
259 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200260 continue;
261 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200262 if (binds[i].pollin) {
263 binds[i].pollin = 0;
264 /* leftover pollin */
265 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100266 break;
267 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100268 pfd[pfd_count].fd = binds[i].sock;
269 pfd[pfd_count].events = POLLIN;
270 pfd[pfd_count].revents = 0;
271
272 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100273 }
274
Michal Vasko0a3f3752016-10-13 14:58:38 +0200275 if (sock == -1) {
276 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100277 sigfillset(&sigmask);
278 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100279 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100280 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
281
Michal Vasko0a3f3752016-10-13 14:58:38 +0200282 if (!ret) {
283 /* we timeouted */
284 free(pfd);
285 return 0;
286 } else if (ret == -1) {
287 ERR("Poll failed (%s).", strerror(errno));
288 free(pfd);
289 return -1;
290 }
Michal Vasko086311b2016-01-08 09:53:11 +0100291
Michal Vaskoac2f6182017-01-30 14:32:03 +0100292 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
293 /* adjust i so that indices in binds and pfd always match */
294 while (binds[i].sock != pfd[j].fd) {
295 ++i;
296 }
297
298 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200299 --ret;
300
301 if (!ret) {
302 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100303 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200304 break;
305 } else {
306 /* just remember the event for next time */
307 binds[i].pollin = 1;
308 }
309 }
Michal Vasko086311b2016-01-08 09:53:11 +0100310 }
311 }
312 free(pfd);
313
314 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100315 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100316 return -1;
317 }
318
319 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100320 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100321 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100322 return -1;
323 }
Michal Vasko6ccb29d2016-10-13 15:00:27 +0200324 VRB("Accepted a connection on %s:%u.", binds[i].address, binds[i].port);
Michal Vasko086311b2016-01-08 09:53:11 +0100325
Michal Vasko0190bc32016-03-02 15:47:49 +0100326 /* make the socket non-blocking */
327 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
328 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100329 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100330 return -1;
331 }
332
Michal Vasko3031aae2016-01-27 16:07:18 +0100333 if (idx) {
334 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100335 }
336
Michal Vasko086311b2016-01-08 09:53:11 +0100337 /* host was requested */
338 if (host) {
339 if (saddr.ss_family == AF_INET) {
340 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100341 if (*host) {
342 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
343 ERR("inet_ntop failed (%s).", strerror(errno));
344 free(*host);
345 *host = NULL;
346 }
Michal Vasko086311b2016-01-08 09:53:11 +0100347
Michal Vasko4eb3c312016-03-01 14:09:37 +0100348 if (port) {
349 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
350 }
351 } else {
352 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100353 }
354 } else if (saddr.ss_family == AF_INET6) {
355 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100356 if (*host) {
357 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
358 ERR("inet_ntop failed (%s).", strerror(errno));
359 free(*host);
360 *host = NULL;
361 }
Michal Vasko086311b2016-01-08 09:53:11 +0100362
Michal Vasko4eb3c312016-03-01 14:09:37 +0100363 if (port) {
364 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
365 }
366 } else {
367 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100368 }
369 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100370 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100371 }
372 }
373
374 return ret;
375}
376
Michal Vasko05ba9df2016-01-13 14:40:27 +0100377static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100378nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100379{
380 const char *identifier = NULL, *version = NULL, *format = NULL;
381 char *model_data = NULL;
382 const struct lys_module *module;
383 struct nc_server_error *err;
384 struct lyd_node *child, *data = NULL;
Michal Vasko88639e92017-08-03 14:38:10 +0200385 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100386
387 LY_TREE_FOR(rpc->child, child) {
388 if (!strcmp(child->schema->name, "identifier")) {
389 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
390 } else if (!strcmp(child->schema->name, "version")) {
391 version = ((struct lyd_node_leaf_list *)child)->value_str;
Radek Krejci1afa7792017-03-26 11:24:16 -0500392 if (version && version[0] == '\0') {
393 version = NULL;
394 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100395 } else if (!strcmp(child->schema->name, "format")) {
396 format = ((struct lyd_node_leaf_list *)child)->value_str;
397 }
398 }
399
400 /* check version */
401 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100402 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
403 nc_err_set_msg(err, "The requested version is not supported.", "en");
404 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100405 }
406
407 /* check and get module with the name identifier */
Radek Krejci3222b7d2017-09-21 16:04:30 +0200408 module = ly_ctx_get_module(server_opts.ctx, identifier, version, 0);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100409 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200410 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
411 }
412 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100413 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
414 nc_err_set_msg(err, "The requested schema was not found.", "en");
415 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100416 }
417
418 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100419 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100420 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
Radek Krejci90fba642016-12-07 15:59:45 +0100421 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100422 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
423 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100424 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
425 nc_err_set_msg(err, "The requested format is not supported.", "en");
426 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100427 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200428 if (!model_data) {
429 ERRINT;
430 return NULL;
431 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100432
Michal Vasko88639e92017-08-03 14:38:10 +0200433 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/data", 1);
434 if (!sdata) {
435 ERRINT;
436 free(model_data);
437 return NULL;
438 }
439
Radek Krejci539efb62016-08-24 15:05:16 +0200440 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
441 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100442 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100443 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200444 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100445 return NULL;
446 }
447
Radek Krejci36dfdb32016-09-01 16:56:35 +0200448 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100449}
450
451static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100452nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100453{
Michal Vasko428087d2016-01-14 16:04:28 +0100454 session->term_reason = NC_SESSION_TERM_CLOSED;
455 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100456}
457
Michal Vasko086311b2016-01-08 09:53:11 +0100458API int
459nc_server_init(struct ly_ctx *ctx)
460{
Michal Vasko88639e92017-08-03 14:38:10 +0200461 const struct lys_node *rpc;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100462
Michal Vasko086311b2016-01-08 09:53:11 +0100463 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200464 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100465 return -1;
466 }
467
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100468 nc_init();
469
Michal Vasko05ba9df2016-01-13 14:40:27 +0100470 /* set default <get-schema> callback if not specified */
Michal Vasko88639e92017-08-03 14:38:10 +0200471 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
472 if (rpc && !rpc->priv) {
473 lys_set_private(rpc, nc_clb_default_get_schema);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100474 }
475
476 /* set default <close-session> callback if not specififed */
Michal Vasko88639e92017-08-03 14:38:10 +0200477 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session", 0);
478 if (rpc && !rpc->priv) {
479 lys_set_private(rpc, nc_clb_default_close_session);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100480 }
481
Michal Vasko086311b2016-01-08 09:53:11 +0100482 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100483
484 server_opts.new_session_id = 1;
485 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
486
Michal Vasko086311b2016-01-08 09:53:11 +0100487 return 0;
488}
489
Michal Vaskob48aa812016-01-18 14:13:09 +0100490API void
491nc_server_destroy(void)
492{
Radek Krejci658782b2016-12-04 22:04:55 +0100493 unsigned int i;
494
495 for (i = 0; i < server_opts.capabilities_count; i++) {
496 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
497 }
498 free(server_opts.capabilities);
Michal Vaskob48aa812016-01-18 14:13:09 +0100499 pthread_spin_destroy(&server_opts.sid_lock);
500
Radek Krejci53691be2016-02-22 13:58:37 +0100501#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100502 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100503#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100504#ifdef NC_ENABLED_SSH
505 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100506
507 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
508 server_opts.hostkey_data_free(server_opts.hostkey_data);
509 }
510#endif
511#ifdef NC_ENABLED_TLS
512 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
513 server_opts.server_cert_data_free(server_opts.server_cert_data);
514 }
515 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
516 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
517 }
Michal Vaskob48aa812016-01-18 14:13:09 +0100518#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100519 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100520}
521
Michal Vasko086311b2016-01-08 09:53:11 +0100522API int
523nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
524{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200525 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
526 ERRARG("basic_mode");
527 return -1;
528 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
529 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100530 return -1;
531 }
532
533 server_opts.wd_basic_mode = basic_mode;
534 server_opts.wd_also_supported = also_supported;
535 return 0;
536}
537
Michal Vasko1a38c862016-01-15 15:50:07 +0100538API void
Michal Vasko55f03972016-04-13 08:56:01 +0200539nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
540{
541 if (!basic_mode && !also_supported) {
542 ERRARG("basic_mode and also_supported");
543 return;
544 }
545
546 if (basic_mode) {
547 *basic_mode = server_opts.wd_basic_mode;
548 }
549 if (also_supported) {
550 *also_supported = server_opts.wd_also_supported;
551 }
552}
553
Michal Vasko55f03972016-04-13 08:56:01 +0200554API int
Radek Krejci658782b2016-12-04 22:04:55 +0100555nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200556{
Radek Krejci658782b2016-12-04 22:04:55 +0100557 const char **new;
558
559 if (!value || !value[0]) {
560 ERRARG("value must not be empty");
561 return EXIT_FAILURE;
562 }
563
564 server_opts.capabilities_count++;
565 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
566 if (!new) {
567 ERRMEM;
568 return EXIT_FAILURE;
569 }
570 server_opts.capabilities = new;
571 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
572
573 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200574}
575
Michal Vasko1a38c862016-01-15 15:50:07 +0100576API void
Michal Vasko086311b2016-01-08 09:53:11 +0100577nc_server_set_hello_timeout(uint16_t hello_timeout)
578{
Michal Vasko086311b2016-01-08 09:53:11 +0100579 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100580}
581
Michal Vasko55f03972016-04-13 08:56:01 +0200582API uint16_t
583nc_server_get_hello_timeout(void)
584{
585 return server_opts.hello_timeout;
586}
587
Michal Vasko1a38c862016-01-15 15:50:07 +0100588API void
Michal Vasko086311b2016-01-08 09:53:11 +0100589nc_server_set_idle_timeout(uint16_t idle_timeout)
590{
Michal Vasko086311b2016-01-08 09:53:11 +0100591 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100592}
593
Michal Vasko55f03972016-04-13 08:56:01 +0200594API uint16_t
595nc_server_get_idle_timeout(void)
596{
597 return server_opts.idle_timeout;
598}
599
Michal Vasko71090fc2016-05-24 16:37:28 +0200600API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100601nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100602{
Michal Vasko71090fc2016-05-24 16:37:28 +0200603 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200604 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200605
Michal Vasko45e53ae2016-04-07 11:46:03 +0200606 if (!server_opts.ctx) {
607 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200608 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200609 } else if (fdin < 0) {
610 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200611 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200612 } else if (fdout < 0) {
613 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200614 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200615 } else if (!username) {
616 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200617 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200618 } else if (!session) {
619 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200620 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100621 }
622
623 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +0100624 *session = nc_new_session(0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100625 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100626 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200627 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100628 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100629 (*session)->status = NC_STATUS_STARTING;
630 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100631
Michal Vaskoade892d2017-02-22 13:40:35 +0100632 /* transport lock */
633 pthread_mutex_init((*session)->ti_lock, NULL);
634 pthread_cond_init((*session)->ti_cond, NULL);
635 *(*session)->ti_inuse = 0;
636
Michal Vasko086311b2016-01-08 09:53:11 +0100637 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100638 (*session)->ti_type = NC_TI_FD;
639 (*session)->ti.fd.in = fdin;
640 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100641
642 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100643 (*session)->flags = NC_SESSION_SHAREDCTX;
644 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100645
Michal Vaskob48aa812016-01-18 14:13:09 +0100646 /* assign new SID atomically */
647 pthread_spin_lock(&server_opts.sid_lock);
648 (*session)->id = server_opts.new_session_id++;
649 pthread_spin_unlock(&server_opts.sid_lock);
650
Michal Vasko086311b2016-01-08 09:53:11 +0100651 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200652 msgtype = nc_handshake(*session);
653 if (msgtype != NC_MSG_HELLO) {
654 nc_session_free(*session, NULL);
655 *session = NULL;
656 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100657 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200658
659 nc_gettimespec_mono(&ts_cur);
660 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
661 nc_gettimespec_real(&ts_cur);
662 (*session)->opts.server.session_start = ts_cur.tv_sec;
663
Michal Vasko1a38c862016-01-15 15:50:07 +0100664 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100665
Michal Vasko71090fc2016-05-24 16:37:28 +0200666 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100667}
Michal Vasko9e036d52016-01-08 10:49:26 +0100668
Michal Vaskob30b99c2016-07-26 11:35:43 +0200669static void
670nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
671{
672 uint8_t i, found = 0;
673
674 for (i = 0; i < ps->queue_len; ++i) {
675 /* idx round buffer adjust */
676 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
677 i = -ps->queue_begin;
678 }
679
680 if (found) {
681 /* move the value back one place */
682 if (ps->queue[ps->queue_begin + i] == id) {
683 /* another equal value, simply cannot be */
684 ERRINT;
685 }
686
687 if (ps->queue_begin + i == 0) {
688 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
689 } else {
690 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
691 }
692 } else if (ps->queue[ps->queue_begin + i] == id) {
693 /* found our id, there can be no more equal valid values */
694 found = 1;
695 }
696 }
697
698 if (!found) {
699 ERRINT;
700 }
701 --ps->queue_len;
702}
703
Michal Vaskof04a52a2016-04-07 10:52:10 +0200704int
Michal Vasko26043172016-07-26 14:08:59 +0200705nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200706{
707 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200708 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200709 struct timespec ts;
710
Michal Vasko77a6abe2017-10-05 10:02:20 +0200711 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100712 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200713
714 /* LOCK */
715 ret = pthread_mutex_timedlock(&ps->lock, &ts);
716 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200717 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200718 return -1;
719 }
720
721 /* get a unique queue value (by adding 1 to the last added value, if any) */
722 if (ps->queue_len) {
723 queue_last = ps->queue_begin + ps->queue_len - 1;
724 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
725 queue_last -= NC_PS_QUEUE_SIZE;
726 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200727 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200728 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200729 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200730 }
731
732 /* add ourselves into the queue */
733 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200734 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200735 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200736 return -1;
737 }
738 ++ps->queue_len;
739 queue_last = ps->queue_begin + ps->queue_len - 1;
740 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
741 queue_last -= NC_PS_QUEUE_SIZE;
742 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200743 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200744
745 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200746 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +0200747 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100748 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200749
750 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
751 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200752 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200753 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200754 nc_ps_queue_remove_id(ps, *id);
755 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200756 return -1;
757 }
758 }
759
Michal Vaskobe86fe32016-04-07 10:43:03 +0200760 /* UNLOCK */
761 pthread_mutex_unlock(&ps->lock);
762
763 return 0;
764}
765
Michal Vaskof04a52a2016-04-07 10:52:10 +0200766int
Michal Vasko26043172016-07-26 14:08:59 +0200767nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200768{
769 int ret;
770 struct timespec ts;
771
Michal Vasko77a6abe2017-10-05 10:02:20 +0200772 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100773 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200774
775 /* LOCK */
776 ret = pthread_mutex_timedlock(&ps->lock, &ts);
777 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200778 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200779 ret = -1;
780 }
781
Michal Vaskob30b99c2016-07-26 11:35:43 +0200782 /* we must be the first, it was our turn after all, right? */
783 if (ps->queue[ps->queue_begin] != id) {
784 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200785 /* UNLOCK */
786 if (!ret) {
787 pthread_mutex_unlock(&ps->lock);
788 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200789 return -1;
790 }
791
Michal Vaskobe86fe32016-04-07 10:43:03 +0200792 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200793 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200794
795 /* broadcast to all other threads that the queue moved */
796 pthread_cond_broadcast(&ps->cond);
797
Michal Vaskobe86fe32016-04-07 10:43:03 +0200798 /* UNLOCK */
799 if (!ret) {
800 pthread_mutex_unlock(&ps->lock);
801 }
802
803 return ret;
804}
805
Michal Vasko428087d2016-01-14 16:04:28 +0100806API struct nc_pollsession *
807nc_ps_new(void)
808{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100809 struct nc_pollsession *ps;
810
811 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100812 if (!ps) {
813 ERRMEM;
814 return NULL;
815 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200816 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100817 pthread_mutex_init(&ps->lock, NULL);
818
819 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100820}
821
822API void
823nc_ps_free(struct nc_pollsession *ps)
824{
fanchanghu3d4e7212017-08-09 09:42:30 +0800825 uint16_t i;
826
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100827 if (!ps) {
828 return;
829 }
830
Michal Vaskobe86fe32016-04-07 10:43:03 +0200831 if (ps->queue_len) {
832 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
833 }
834
fanchanghu3d4e7212017-08-09 09:42:30 +0800835 for (i = 0; i < ps->session_count; i++) {
836 free(ps->sessions[i]);
837 }
838
Michal Vasko428087d2016-01-14 16:04:28 +0100839 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100840 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200841 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100842
Michal Vasko428087d2016-01-14 16:04:28 +0100843 free(ps);
844}
845
846API int
847nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
848{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200849 uint8_t q_id;
850
Michal Vasko45e53ae2016-04-07 11:46:03 +0200851 if (!ps) {
852 ERRARG("ps");
853 return -1;
854 } else if (!session) {
855 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100856 return -1;
857 }
858
Michal Vasko48a63ed2016-03-01 09:48:21 +0100859 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200860 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200861 return -1;
862 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100863
Michal Vasko428087d2016-01-14 16:04:28 +0100864 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100865 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +0100866 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100867 ERRMEM;
868 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200869 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100870 return -1;
871 }
fanchanghu3d4e7212017-08-09 09:42:30 +0800872 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
873 if (!ps->sessions[ps->session_count - 1]) {
874 ERRMEM;
875 --ps->session_count;
876 /* UNLOCK */
877 nc_ps_unlock(ps, q_id, __func__);
878 return -1;
879 }
880 ps->sessions[ps->session_count - 1]->session = session;
881 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +0100882
Michal Vasko48a63ed2016-03-01 09:48:21 +0100883 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200884 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100885}
886
Michal Vasko48a63ed2016-03-01 09:48:21 +0100887static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100888_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100889{
890 uint16_t i;
891
Radek Krejcid5f978f2016-03-03 13:14:45 +0100892 if (index >= 0) {
893 i = (uint16_t)index;
894 goto remove;
895 }
Michal Vasko428087d2016-01-14 16:04:28 +0100896 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +0800897 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100898remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100899 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +0800900 if (i <= ps->session_count) {
901 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +0100902 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +0800903 }
904 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +0100905 free(ps->sessions);
906 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +0100907 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +0100908 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100909 return 0;
910 }
911 }
912
Michal Vaskof0537d82016-01-29 14:42:38 +0100913 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100914}
915
Michal Vasko48a63ed2016-03-01 09:48:21 +0100916API int
917nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
918{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200919 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200920 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100921
Michal Vasko45e53ae2016-04-07 11:46:03 +0200922 if (!ps) {
923 ERRARG("ps");
924 return -1;
925 } else if (!session) {
926 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100927 return -1;
928 }
929
930 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200931 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200932 return -1;
933 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100934
Radek Krejcid5f978f2016-03-03 13:14:45 +0100935 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100936
937 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200938 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100939
Michal Vaskobe86fe32016-04-07 10:43:03 +0200940 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100941}
942
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100943API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +0200944nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100945{
946 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100947 struct nc_session *ret = NULL;
948
949 if (!ps) {
950 ERRARG("ps");
951 return NULL;
952 }
953
954 /* LOCK */
955 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
956 return NULL;
957 }
958
Michal Vasko4871c9d2017-10-09 14:48:39 +0200959 if (idx < ps->session_count) {
960 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100961 }
962
963 /* UNLOCK */
964 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
965
966 return ret;
967}
968
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100969API uint16_t
970nc_ps_session_count(struct nc_pollsession *ps)
971{
972 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200973 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100974 return 0;
975 }
976
Michal Vaskof4462fd2017-02-15 14:29:05 +0100977 return ps->session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100978}
979
Michal Vasko71090fc2016-05-24 16:37:28 +0200980/* must be called holding the session lock!
981 * returns: NC_PSPOLL_ERROR,
982 * NC_PSPOLL_BAD_RPC,
983 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
984 * NC_PSPOLL_RPC
985 */
986static int
Radek Krejci93e80222016-10-03 13:34:25 +0200987nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100988{
989 struct lyxml_elem *xml = NULL;
990 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200991 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200992 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100993
Michal Vasko45e53ae2016-04-07 11:46:03 +0200994 if (!session) {
995 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200996 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200997 } else if (!rpc) {
998 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200999 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001000 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001001 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001002 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001003 }
1004
1005 msgtype = nc_read_msg(session, &xml);
1006
1007 switch (msgtype) {
1008 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001009 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001010 if (!*rpc) {
1011 ERRMEM;
1012 goto error;
1013 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001014
Radek Krejcif93c7d42016-04-06 13:41:15 +02001015 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001016 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1017 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001018 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001019 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +02001020 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +02001021 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001022 nc_server_reply_free(reply);
1023 if (ret == -1) {
1024 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001025 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001026 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1027 } else {
1028 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001029 }
Michal Vasko428087d2016-01-14 16:04:28 +01001030 (*rpc)->root = xml;
1031 break;
1032 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001033 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001034 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001035 goto error;
1036 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001037 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001038 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001039 goto error;
1040 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001041 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001042 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001043 goto error;
1044 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001045 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +01001046 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
1047 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001048 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001049 break;
1050 }
1051
Michal Vasko71090fc2016-05-24 16:37:28 +02001052 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001053
1054error:
1055 /* cleanup */
1056 lyxml_free(server_opts.ctx, xml);
1057
Michal Vasko71090fc2016-05-24 16:37:28 +02001058 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001059}
1060
fanchanghu966f2de2016-07-21 02:28:57 -04001061API void
1062nc_set_global_rpc_clb(nc_rpc_clb clb)
1063{
1064 global_rpc_clb = clb;
1065}
1066
Radek Krejci93e80222016-10-03 13:34:25 +02001067API NC_MSG_TYPE
1068nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1069{
1070 NC_MSG_TYPE result = NC_MSG_NOTIF;
1071 int ret;
1072
1073 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001074 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001075 ERRARG("session");
1076 return NC_MSG_ERROR;
1077 } else if (!notif || !notif->tree || !notif->eventtime) {
1078 ERRARG("notif");
1079 return NC_MSG_ERROR;
1080 }
1081
1082 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vaskoade892d2017-02-22 13:40:35 +01001083 ret = nc_session_lock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001084 if (ret < 0) {
1085 return NC_MSG_ERROR;
1086 } else if (!ret) {
1087 return NC_MSG_WOULDBLOCK;
1088 }
1089
1090 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1091 if (ret == -1) {
1092 ERR("Session %u: failed to write notification.", session->id);
1093 result = NC_MSG_ERROR;
1094 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001095
1096 nc_session_unlock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001097
1098 return result;
1099}
1100
Michal Vasko71090fc2016-05-24 16:37:28 +02001101/* must be called holding the session lock!
1102 * returns: NC_PSPOLL_ERROR,
1103 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1104 * NC_PSPOLL_REPLY_ERROR,
1105 * 0
1106 */
1107static int
Radek Krejci93e80222016-10-03 13:34:25 +02001108nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001109{
1110 nc_rpc_clb clb;
1111 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001112 struct lys_node *rpc_act = NULL;
1113 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001114 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001115
Michal Vasko4a827e52016-03-03 10:59:00 +01001116 if (!rpc) {
1117 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001118 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001119 }
1120
Michal Vasko90e8e692016-07-13 12:27:57 +02001121 if (rpc->tree->schema->nodetype == LYS_RPC) {
1122 /* RPC */
1123 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001124 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001125 /* action */
1126 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1127 if (elem->schema->nodetype == LYS_ACTION) {
1128 rpc_act = elem->schema;
1129 break;
1130 }
1131 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001132 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001133 if (!rpc_act) {
1134 ERRINT;
1135 return NC_PSPOLL_ERROR;
1136 }
1137 }
1138
1139 if (!rpc_act->priv) {
1140 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001141 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001142 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001143 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001144 reply = clb(rpc->tree, session);
1145 }
1146
1147 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001148 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001149 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001150 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1151 if (reply->type == NC_RPL_ERROR) {
1152 ret |= NC_PSPOLL_REPLY_ERROR;
1153 }
1154 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001155
Michal Vasko71090fc2016-05-24 16:37:28 +02001156 if (r == -1) {
1157 ERR("Session %u: failed to write reply.", session->id);
1158 ret |= NC_PSPOLL_ERROR;
1159 }
Michal Vasko428087d2016-01-14 16:04:28 +01001160
1161 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1162 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1163 session->status = NC_STATUS_INVALID;
1164 }
1165
Michal Vasko71090fc2016-05-24 16:37:28 +02001166 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001167}
1168
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001169/* session must be running and session lock held!
1170 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1171 * NC_PSPOLL_ERROR, (msg filled)
1172 * NC_PSPOLL_TIMEOUT,
1173 * NC_PSPOLL_RPC (some application data available),
1174 * NC_PSPOLL_SSH_CHANNEL,
1175 * NC_PSPOLL_SSH_MSG
1176 */
1177static int
Michal Vasko9fb42272017-10-05 13:50:05 +02001178nc_ps_poll_session(struct nc_session *session, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001179{
Michal Vasko9a327362017-01-11 11:31:46 +01001180 struct pollfd pfd;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001181 int r, ret;
Michal Vasko9a327362017-01-11 11:31:46 +01001182#ifdef NC_ENABLED_SSH
1183 struct nc_session *new;
1184#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001185
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001186 /* check timeout first */
1187 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
Michal Vasko9fb42272017-10-05 13:50:05 +02001188 && (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001189 sprintf(msg, "session idle timeout elapsed");
1190 session->status = NC_STATUS_INVALID;
1191 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1192 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1193 }
1194
1195 switch (session->ti_type) {
1196#ifdef NC_ENABLED_SSH
1197 case NC_TI_LIBSSH:
1198 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1199 if (r < 1) {
1200 if (r == SSH_EOF) {
1201 sprintf(msg, "SSH channel unexpected EOF");
1202 session->status = NC_STATUS_INVALID;
1203 session->term_reason = NC_SESSION_TERM_DROPPED;
1204 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1205 } else if (r == SSH_ERROR) {
1206 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
1207 session->status = NC_STATUS_INVALID;
1208 session->term_reason = NC_SESSION_TERM_OTHER;
1209 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1210 } else {
1211 ret = NC_PSPOLL_TIMEOUT;
1212 }
1213 break;
1214 }
1215
1216 /* we have some data, but it may be just an SSH message */
1217 r = ssh_execute_message_callbacks(session->ti.libssh.session);
1218 if (r != SSH_OK) {
1219 sprintf(msg, "failed to receive SSH messages (%s)", ssh_get_error(session->ti.libssh.session));
1220 session->status = NC_STATUS_INVALID;
1221 session->term_reason = NC_SESSION_TERM_OTHER;
1222 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1223 } else if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1224 /* new SSH message */
1225 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1226 if (session->ti.libssh.next) {
1227 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1228 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1229 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1230 /* new NETCONF SSH channel */
1231 ret = NC_PSPOLL_SSH_CHANNEL;
1232 break;
1233 }
1234 }
1235 if (new != session) {
1236 break;
1237 }
1238 }
1239
1240 /* just some SSH message */
1241 ret = NC_PSPOLL_SSH_MSG;
1242 } else {
1243 /* we have some application data */
1244 ret = NC_PSPOLL_RPC;
1245 }
1246 break;
1247#endif
1248#ifdef NC_ENABLED_TLS
1249 case NC_TI_OPENSSL:
1250 r = SSL_pending(session->ti.tls);
1251 if (!r) {
1252 /* no data pending in the SSL buffer, poll fd */
1253 pfd.fd = SSL_get_rfd(session->ti.tls);
1254 if (pfd.fd < 0) {
1255 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1256 ret = NC_PSPOLL_ERROR;
1257 break;
1258 }
1259 pfd.events = POLLIN;
1260 pfd.revents = 0;
1261 r = poll(&pfd, 1, 0);
1262
1263 if ((r < 0) && (errno != EINTR)) {
1264 sprintf(msg, "poll failed (%s)", strerror(errno));
1265 session->status = NC_STATUS_INVALID;
1266 ret = NC_PSPOLL_ERROR;
1267 } else if (r > 0) {
1268 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1269 sprintf(msg, "communication socket unexpectedly closed");
1270 session->status = NC_STATUS_INVALID;
1271 session->term_reason = NC_SESSION_TERM_DROPPED;
1272 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1273 } else if (pfd.revents & POLLERR) {
1274 sprintf(msg, "communication socket error");
1275 session->status = NC_STATUS_INVALID;
1276 session->term_reason = NC_SESSION_TERM_OTHER;
1277 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1278 } else {
1279 ret = NC_PSPOLL_RPC;
1280 }
1281 } else {
1282 ret = NC_PSPOLL_TIMEOUT;
1283 }
1284 } else {
1285 ret = NC_PSPOLL_RPC;
1286 }
1287 break;
1288#endif
1289 case NC_TI_FD:
1290 pfd.fd = session->ti.fd.in;
1291 pfd.events = POLLIN;
1292 pfd.revents = 0;
1293 r = poll(&pfd, 1, 0);
1294
1295 if ((r < 0) && (errno != EINTR)) {
1296 sprintf(msg, "poll failed (%s)", strerror(errno));
1297 session->status = NC_STATUS_INVALID;
1298 ret = NC_PSPOLL_ERROR;
1299 } else if (r > 0) {
1300 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1301 sprintf(msg, "communication socket unexpectedly closed");
1302 session->status = NC_STATUS_INVALID;
1303 session->term_reason = NC_SESSION_TERM_DROPPED;
1304 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1305 } else if (pfd.revents & POLLERR) {
1306 sprintf(msg, "communication socket error");
1307 session->status = NC_STATUS_INVALID;
1308 session->term_reason = NC_SESSION_TERM_OTHER;
1309 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1310 } else {
1311 ret = NC_PSPOLL_RPC;
1312 }
1313 } else {
1314 ret = NC_PSPOLL_TIMEOUT;
1315 }
1316 break;
1317 case NC_TI_NONE:
1318 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1319 ret = NC_PSPOLL_ERROR;
1320 break;
1321 }
1322
1323 return ret;
1324}
1325
1326API int
1327nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1328{
1329 int ret, r;
1330 uint8_t q_id;
1331 uint16_t i, j;
1332 char msg[256];
1333 struct timespec ts_timeout, ts_cur;
1334 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001335 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001336 struct nc_server_rpc *rpc = NULL;
1337
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001338 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001339 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001340 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001341 }
1342
Michal Vaskoade892d2017-02-22 13:40:35 +01001343 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001344 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001345 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001346 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001347
Michal Vaskoade892d2017-02-22 13:40:35 +01001348 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001349 nc_ps_unlock(ps, q_id, __func__);
1350 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001351 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001352
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001353 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001354 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001355 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001356 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001357 nc_addtimespec(&ts_timeout, timeout);
1358 }
1359
1360 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001361 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001362 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001363 if (ps->last_event_session == ps->session_count - 1) {
1364 i = j = 0;
1365 } else {
1366 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001367 }
Michal Vasko9a327362017-01-11 11:31:46 +01001368 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001369 cur_ps_session = ps->sessions[i];
1370 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001371
Michal Vaskoade892d2017-02-22 13:40:35 +01001372 /* SESSION LOCK */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001373 r = nc_session_lock(cur_session, 0, __func__);
1374 if (r == -1) {
1375 ret = NC_PSPOLL_ERROR;
1376 } else if (r == 1) {
1377 /* no one else is currently working with the session, so we can, otherwise skip it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001378 if (cur_ps_session->state == NC_PS_STATE_NONE) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001379 if (cur_session->status == NC_STATUS_RUNNING) {
1380 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001381 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001382
1383 ret = nc_ps_poll_session(cur_session, ts_cur.tv_sec, msg);
1384 switch (ret) {
1385 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1386 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001387 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001388 break;
1389 case NC_PSPOLL_ERROR:
1390 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001391 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001392 break;
1393 case NC_PSPOLL_TIMEOUT:
1394#ifdef NC_ENABLED_SSH
1395 case NC_PSPOLL_SSH_CHANNEL:
1396 case NC_PSPOLL_SSH_MSG:
1397#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001398 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001399 break;
1400 case NC_PSPOLL_RPC:
1401 /* let's keep the state busy, we are not done with this session */
1402 break;
1403 }
1404 } else {
1405 /* session is not fine, let the caller know */
1406 ret = NC_PSPOLL_SESSION_TERM;
1407 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1408 ret |= NC_PSPOLL_SESSION_ERROR;
1409 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001410 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001411 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001412 } else if (cur_ps_session->state == NC_PS_STATE_BUSY) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001413 /* it definitely should not be busy because we have the lock */
1414 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001415 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001416 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001417
1418 /* keep the session locked only in this one case */
1419 if (ret != NC_PSPOLL_RPC) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001420 /* SESSION UNLOCK */
1421 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001422 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001423 } else {
1424 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001425 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001426 }
Michal Vasko428087d2016-01-14 16:04:28 +01001427
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001428 /* something happened */
1429 if (ret != NC_PSPOLL_TIMEOUT) {
1430 break;
1431 }
1432
Michal Vasko9a327362017-01-11 11:31:46 +01001433 if (i == ps->session_count - 1) {
1434 i = 0;
1435 } else {
1436 ++i;
1437 }
1438 } while (i != j);
1439
Michal Vaskoade892d2017-02-22 13:40:35 +01001440 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001441 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001442 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001443 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001444 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001445
1446 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1447 /* final timeout */
1448 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001449 }
Michal Vasko428087d2016-01-14 16:04:28 +01001450 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001451 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001452
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001453 /* do we want to return the session? */
1454 switch (ret) {
1455 case NC_PSPOLL_RPC:
1456 case NC_PSPOLL_SESSION_TERM:
1457 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1458#ifdef NC_ENABLED_SSH
1459 case NC_PSPOLL_SSH_CHANNEL:
1460 case NC_PSPOLL_SSH_MSG:
1461#endif
1462 if (session) {
1463 *session = cur_session;
1464 }
1465 ps->last_event_session = i;
1466 break;
1467 default:
1468 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001469 }
Michal Vasko428087d2016-01-14 16:04:28 +01001470
Michal Vaskoade892d2017-02-22 13:40:35 +01001471 /* PS UNLOCK */
1472 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001473
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001474 /* we have some data available and the session is locked */
1475 if (ret == NC_PSPOLL_RPC) {
1476 ret = nc_server_recv_rpc(cur_session, &rpc);
1477 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1478 if (cur_session->status != NC_STATUS_RUNNING) {
1479 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001480 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001481 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001482 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001483 }
1484 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001485 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001486
1487 /* process RPC, not needed afterwards */
1488 ret |= nc_server_send_reply(cur_session, rpc);
1489 nc_server_rpc_free(rpc, server_opts.ctx);
1490
1491 if (cur_session->status != NC_STATUS_RUNNING) {
1492 ret |= NC_PSPOLL_SESSION_TERM;
1493 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1494 ret |= NC_PSPOLL_SESSION_ERROR;
1495 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001496 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001497 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001498 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001499 }
Michal Vasko428087d2016-01-14 16:04:28 +01001500 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001501
1502 /* SESSION UNLOCK */
1503 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001504 }
1505
Michal Vasko48a63ed2016-03-01 09:48:21 +01001506 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001507}
1508
Michal Vaskod09eae62016-02-01 10:32:52 +01001509API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001510nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001511{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001512 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001513 uint16_t i;
1514 struct nc_session *session;
1515
Michal Vasko9a25e932016-02-01 10:36:42 +01001516 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001517 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001518 return;
1519 }
1520
Michal Vasko48a63ed2016-03-01 09:48:21 +01001521 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001522 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001523 return;
1524 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001525
Michal Vasko48a63ed2016-03-01 09:48:21 +01001526 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001527 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001528 nc_session_free(ps->sessions[i]->session, data_free);
1529 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001530 }
1531 free(ps->sessions);
1532 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001533 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001534 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001535 } else {
1536 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001537 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1538 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001539 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001540 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001541 continue;
1542 }
1543
1544 ++i;
1545 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001546 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001547
1548 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001549 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001550}
1551
Radek Krejci53691be2016-02-22 13:58:37 +01001552#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001553
Michal Vaskoe2713da2016-08-22 16:06:40 +02001554API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001555nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001556{
Michal Vasko3031aae2016-01-27 16:07:18 +01001557 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001558 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001559
Michal Vasko45e53ae2016-04-07 11:46:03 +02001560 if (!name) {
1561 ERRARG("name");
1562 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001563 }
1564
Michal Vaskoade892d2017-02-22 13:40:35 +01001565 /* BIND LOCK */
1566 pthread_mutex_lock(&server_opts.bind_lock);
1567
1568 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001569 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001570
1571 /* check name uniqueness */
1572 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001573 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001574 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001575 ret = -1;
1576 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001577 }
1578 }
1579
Michal Vasko3031aae2016-01-27 16:07:18 +01001580 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001581 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001582 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001583 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001584 ret = -1;
1585 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001586 }
1587 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001588 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001589
Michal Vaskoe2713da2016-08-22 16:06:40 +02001590 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001591 if (!server_opts.binds) {
1592 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001593 ret = -1;
1594 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001595 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001596
Michal Vasko2e6defd2016-10-07 15:48:15 +02001597 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1598 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1599 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001600 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001601
1602 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001603#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001604 case NC_TI_LIBSSH:
1605 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1606 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1607 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001608 ret = -1;
1609 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001610 }
1611 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1612 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1613 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1614 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1615 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001616#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001617#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001618 case NC_TI_OPENSSL:
1619 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1620 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1621 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001622 ret = -1;
1623 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001624 }
1625 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001626#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001627 default:
1628 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001629 ret = -1;
1630 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001631 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001632
Michal Vaskoade892d2017-02-22 13:40:35 +01001633cleanup:
1634 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001635 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001636
Michal Vaskoade892d2017-02-22 13:40:35 +01001637 /* BIND UNLOCK */
1638 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001639
Michal Vaskoade892d2017-02-22 13:40:35 +01001640 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001641}
1642
Michal Vasko3031aae2016-01-27 16:07:18 +01001643int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001644nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001645{
1646 struct nc_endpt *endpt;
1647 struct nc_bind *bind = NULL;
1648 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001649 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001650
Michal Vasko45e53ae2016-04-07 11:46:03 +02001651 if (!endpt_name) {
1652 ERRARG("endpt_name");
1653 return -1;
1654 } else if ((!address && !port) || (address && port)) {
1655 ERRARG("address and port");
1656 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001657 }
1658
Michal Vaskoe2713da2016-08-22 16:06:40 +02001659 if (address) {
1660 set_addr = 1;
1661 } else {
1662 set_addr = 0;
1663 }
1664
Michal Vaskoade892d2017-02-22 13:40:35 +01001665 /* BIND LOCK */
1666 pthread_mutex_lock(&server_opts.bind_lock);
1667
1668 /* ENDPT LOCK */
1669 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001670 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001671 /* BIND UNLOCK */
1672 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001673 return -1;
1674 }
1675
Michal Vaskoe2713da2016-08-22 16:06:40 +02001676 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001677
Michal Vaskoe2713da2016-08-22 16:06:40 +02001678 if (set_addr) {
1679 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001680 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001681 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001682 }
1683
Michal Vaskoe2713da2016-08-22 16:06:40 +02001684 /* we have all the information we need to create a listening socket */
1685 if (address && port) {
1686 /* create new socket, close the old one */
1687 sock = nc_sock_listen(address, port);
1688 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001689 ret = -1;
1690 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001691 }
1692
1693 if (bind->sock > -1) {
1694 close(bind->sock);
1695 }
1696 bind->sock = sock;
1697 } /* else we are just setting address or port */
1698
1699 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001700 lydict_remove(server_opts.ctx, bind->address);
1701 bind->address = lydict_insert(server_opts.ctx, address, 0);
1702 } else {
1703 bind->port = port;
1704 }
1705
Michal Vaskoe2713da2016-08-22 16:06:40 +02001706 if (sock > -1) {
1707#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001708 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001709#elif defined(NC_ENABLED_SSH)
1710 VRB("Listening on %s:%u for SSH connections.", address, port);
1711#else
1712 VRB("Listening on %s:%u for TLS connections.", address, port);
1713#endif
1714 }
1715
Michal Vasko4c1fb492017-01-30 14:31:07 +01001716cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001717 /* ENDPT UNLOCK */
1718 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001719
Michal Vaskoade892d2017-02-22 13:40:35 +01001720 /* BIND UNLOCK */
1721 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001722
Michal Vasko4c1fb492017-01-30 14:31:07 +01001723 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001724}
1725
Michal Vaskoe2713da2016-08-22 16:06:40 +02001726API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001727nc_server_endpt_set_address(const char *endpt_name, const char *address)
1728{
1729 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1730}
1731
1732API int
1733nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1734{
1735 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1736}
1737
1738API int
Michal Vasko59050372016-11-22 14:33:55 +01001739nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001740{
1741 uint32_t i;
1742 int ret = -1;
1743
Michal Vaskoade892d2017-02-22 13:40:35 +01001744 /* BIND LOCK */
1745 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001746
Michal Vaskoade892d2017-02-22 13:40:35 +01001747 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001748 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001749
Michal Vasko59050372016-11-22 14:33:55 +01001750 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001751 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001752 for (i = 0; i < server_opts.endpt_count; ++i) {
1753 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001754 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001755#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001756 case NC_TI_LIBSSH:
1757 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1758 free(server_opts.endpts[i].opts.ssh);
1759 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001760#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001761#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001762 case NC_TI_OPENSSL:
1763 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1764 free(server_opts.endpts[i].opts.tls);
1765 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001766#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001767 default:
1768 ERRINT;
1769 /* won't get here ...*/
1770 break;
1771 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001772 ret = 0;
1773 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001774 free(server_opts.endpts);
1775 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001776
1777 /* remove all binds */
1778 for (i = 0; i < server_opts.endpt_count; ++i) {
1779 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1780 if (server_opts.binds[i].sock > -1) {
1781 close(server_opts.binds[i].sock);
1782 }
1783 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001784 free(server_opts.binds);
1785 server_opts.binds = NULL;
1786
Michal Vasko3031aae2016-01-27 16:07:18 +01001787 server_opts.endpt_count = 0;
1788
Michal Vasko1a38c862016-01-15 15:50:07 +01001789 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001790 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001791 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001792 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001793 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001794 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001795 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001796#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001797 case NC_TI_LIBSSH:
1798 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1799 free(server_opts.endpts[i].opts.ssh);
1800 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001801#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001802#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001803 case NC_TI_OPENSSL:
1804 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1805 free(server_opts.endpts[i].opts.tls);
1806 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001807#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001808 default:
1809 ERRINT;
1810 break;
1811 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001812
Michal Vaskoe2713da2016-08-22 16:06:40 +02001813 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001814 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1815 if (server_opts.binds[i].sock > -1) {
1816 close(server_opts.binds[i].sock);
1817 }
1818
1819 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001820 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001821 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001822 free(server_opts.binds);
1823 server_opts.binds = NULL;
1824 free(server_opts.endpts);
1825 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001826 } else if (i < server_opts.endpt_count) {
1827 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1828 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001829 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001830
1831 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001832 if (name) {
1833 break;
1834 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001835 }
1836 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001837 }
1838
Michal Vaskoade892d2017-02-22 13:40:35 +01001839 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001840 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001841
Michal Vaskoade892d2017-02-22 13:40:35 +01001842 /* BIND UNLOCK */
1843 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001844
1845 return ret;
1846}
1847
Michal Vasko71090fc2016-05-24 16:37:28 +02001848API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001849nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001850{
Michal Vasko71090fc2016-05-24 16:37:28 +02001851 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001852 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001853 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001854 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02001855 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01001856
Michal Vasko45e53ae2016-04-07 11:46:03 +02001857 if (!server_opts.ctx) {
1858 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001859 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001860 } else if (!session) {
1861 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001862 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001863 }
1864
Michal Vaskoade892d2017-02-22 13:40:35 +01001865 /* BIND LOCK */
1866 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001867
1868 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001869 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01001870 /* BIND UNLOCK */
1871 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001872 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001873 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001874
Michal Vaskoe2713da2016-08-22 16:06:40 +02001875 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001876 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001877 /* BIND UNLOCK */
1878 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001879 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001880 if (!ret) {
1881 return NC_MSG_WOULDBLOCK;
1882 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001883 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001884 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001885
1886 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
1887 /* ENDPT READ LOCK */
1888 pthread_rwlock_rdlock(&server_opts.endpt_lock);
1889
1890 /* BIND UNLOCK */
1891 pthread_mutex_unlock(&server_opts.bind_lock);
1892
Michal Vaskob48aa812016-01-18 14:13:09 +01001893 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001894
Michal Vaskoade892d2017-02-22 13:40:35 +01001895 *session = nc_new_session(0);
Michal Vasko686aa312016-01-21 15:58:18 +01001896 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001897 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001898 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001899 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001900 msgtype = NC_MSG_ERROR;
1901 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001902 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001903 (*session)->status = NC_STATUS_STARTING;
1904 (*session)->side = NC_SERVER;
1905 (*session)->ctx = server_opts.ctx;
1906 (*session)->flags = NC_SESSION_SHAREDCTX;
1907 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1908 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001909
1910 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001911 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001912 pthread_cond_init((*session)->ti_cond, NULL);
1913 *(*session)->ti_inuse = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001914
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001915 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001916#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001917 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1918 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001919 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001920 if (ret < 0) {
1921 msgtype = NC_MSG_ERROR;
1922 goto cleanup;
1923 } else if (!ret) {
1924 msgtype = NC_MSG_WOULDBLOCK;
1925 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001926 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001927 } else
1928#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001929#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001930 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1931 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001932 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001933 if (ret < 0) {
1934 msgtype = NC_MSG_ERROR;
1935 goto cleanup;
1936 } else if (!ret) {
1937 msgtype = NC_MSG_WOULDBLOCK;
1938 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001939 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001940 } else
1941#endif
1942 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001943 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001944 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001945 msgtype = NC_MSG_ERROR;
1946 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001947 }
1948
Michal Vasko2cc4c682016-03-01 09:16:48 +01001949 (*session)->data = NULL;
1950
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 Vaskob48aa812016-01-18 14:13:09 +01001954 /* assign new SID atomically */
1955 /* LOCK */
1956 pthread_spin_lock(&server_opts.sid_lock);
1957 (*session)->id = server_opts.new_session_id++;
1958 /* UNLOCK */
1959 pthread_spin_unlock(&server_opts.sid_lock);
1960
Michal Vasko9e036d52016-01-08 10:49:26 +01001961 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001962 msgtype = nc_handshake(*session);
1963 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001964 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001965 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001966 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001967 }
Michal Vasko9fb42272017-10-05 13:50:05 +02001968
1969 nc_gettimespec_mono(&ts_cur);
1970 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
1971 nc_gettimespec_real(&ts_cur);
1972 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01001973 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001974
Michal Vasko71090fc2016-05-24 16:37:28 +02001975 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001976
Michal Vasko71090fc2016-05-24 16:37:28 +02001977cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001978 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001979 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001980
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001981 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001982 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001983 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001984}
1985
Michal Vasko2e6defd2016-10-07 15:48:15 +02001986API int
1987nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1988{
1989 uint16_t i;
1990
1991 if (!name) {
1992 ERRARG("name");
1993 return -1;
1994 } else if (!ti) {
1995 ERRARG("ti");
1996 return -1;
1997 }
1998
1999 /* WRITE LOCK */
2000 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2001
2002 /* check name uniqueness */
2003 for (i = 0; i < server_opts.ch_client_count; ++i) {
2004 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2005 ERR("Call Home client \"%s\" already exists.", name);
2006 /* WRITE UNLOCK */
2007 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2008 return -1;
2009 }
2010 }
2011
2012 ++server_opts.ch_client_count;
2013 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2014 if (!server_opts.ch_clients) {
2015 ERRMEM;
2016 /* WRITE UNLOCK */
2017 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2018 return -1;
2019 }
2020 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
2021 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002022 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
2023 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002024
2025 switch (ti) {
2026#ifdef NC_ENABLED_SSH
2027 case NC_TI_LIBSSH:
2028 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2029 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
2030 ERRMEM;
2031 /* WRITE UNLOCK */
2032 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2033 return -1;
2034 }
2035 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
2036 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2037 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
2038 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
2039 break;
2040#endif
2041#ifdef NC_ENABLED_TLS
2042 case NC_TI_OPENSSL:
2043 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2044 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
2045 ERRMEM;
2046 /* WRITE UNLOCK */
2047 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2048 return -1;
2049 }
2050 break;
2051#endif
2052 default:
2053 ERRINT;
2054 /* WRITE UNLOCK */
2055 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2056 return -1;
2057 }
2058
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002059 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
2060
Michal Vasko2e6defd2016-10-07 15:48:15 +02002061 /* set CH default options */
2062 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
2063 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
2064
2065 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
2066
2067 /* WRITE UNLOCK */
2068 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2069
2070 return 0;
2071}
2072
2073API int
Michal Vasko59050372016-11-22 14:33:55 +01002074nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002075{
2076 uint16_t i, j;
2077 int ret = -1;
2078
2079 /* WRITE LOCK */
2080 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2081
Michal Vasko59050372016-11-22 14:33:55 +01002082 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002083 /* remove all CH clients */
2084 for (i = 0; i < server_opts.ch_client_count; ++i) {
2085 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2086
2087 /* remove all endpoints */
2088 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2089 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2090 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2091 }
2092 free(server_opts.ch_clients[i].ch_endpts);
2093
2094 switch (server_opts.ch_clients[i].ti) {
2095#ifdef NC_ENABLED_SSH
2096 case NC_TI_LIBSSH:
2097 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2098 free(server_opts.ch_clients[i].opts.ssh);
2099 break;
2100#endif
2101#ifdef NC_ENABLED_TLS
2102 case NC_TI_OPENSSL:
2103 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2104 free(server_opts.ch_clients[i].opts.tls);
2105 break;
2106#endif
2107 default:
2108 ERRINT;
2109 /* won't get here ...*/
2110 break;
2111 }
2112
2113 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2114
2115 ret = 0;
2116 }
2117 free(server_opts.ch_clients);
2118 server_opts.ch_clients = NULL;
2119
2120 server_opts.ch_client_count = 0;
2121
2122 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002123 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002124 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002125 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 +02002126 /* remove endpt */
2127 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2128
2129 switch (server_opts.ch_clients[i].ti) {
2130#ifdef NC_ENABLED_SSH
2131 case NC_TI_LIBSSH:
2132 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2133 free(server_opts.ch_clients[i].opts.ssh);
2134 break;
2135#endif
2136#ifdef NC_ENABLED_TLS
2137 case NC_TI_OPENSSL:
2138 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2139 free(server_opts.ch_clients[i].opts.tls);
2140 break;
2141#endif
2142 default:
2143 ERRINT;
2144 break;
2145 }
2146
2147 /* remove all endpoints */
2148 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2149 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2150 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2151 }
2152 free(server_opts.ch_clients[i].ch_endpts);
2153
2154 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2155
2156 /* move last client and endpoint(s) to the empty space */
2157 --server_opts.ch_client_count;
2158 if (i < server_opts.ch_client_count) {
2159 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2160 sizeof *server_opts.ch_clients);
2161 } else if (!server_opts.ch_client_count) {
2162 free(server_opts.ch_clients);
2163 server_opts.ch_clients = NULL;
2164 }
2165
2166 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002167 if (name) {
2168 break;
2169 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002170 }
2171 }
2172 }
2173
2174 /* WRITE UNLOCK */
2175 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2176
2177 return ret;
2178}
2179
2180API int
2181nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2182{
2183 uint16_t i;
2184 struct nc_ch_client *client;
2185
2186 if (!client_name) {
2187 ERRARG("client_name");
2188 return -1;
2189 } else if (!endpt_name) {
2190 ERRARG("endpt_name");
2191 return -1;
2192 }
2193
2194 /* LOCK */
2195 client = nc_server_ch_client_lock(client_name, 0, NULL);
2196 if (!client) {
2197 return -1;
2198 }
2199
2200 for (i = 0; i < client->ch_endpt_count; ++i) {
2201 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2202 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2203 /* UNLOCK */
2204 nc_server_ch_client_unlock(client);
2205 return -1;
2206 }
2207 }
2208
2209 ++client->ch_endpt_count;
2210 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2211 if (!client->ch_endpts) {
2212 ERRMEM;
2213 /* UNLOCK */
2214 nc_server_ch_client_unlock(client);
2215 return -1;
2216 }
2217
2218 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2219 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2220 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
2221
2222 /* UNLOCK */
2223 nc_server_ch_client_unlock(client);
2224
2225 return 0;
2226}
2227
2228API int
2229nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2230{
2231 uint16_t i;
2232 int ret = -1;
2233 struct nc_ch_client *client;
2234
2235 if (!client_name) {
2236 ERRARG("client_name");
2237 return -1;
2238 }
2239
2240 /* LOCK */
2241 client = nc_server_ch_client_lock(client_name, 0, NULL);
2242 if (!client) {
2243 return -1;
2244 }
2245
2246 if (!endpt_name) {
2247 /* remove all endpoints */
2248 for (i = 0; i < client->ch_endpt_count; ++i) {
2249 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2250 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2251 }
2252 free(client->ch_endpts);
2253 client->ch_endpts = NULL;
2254 client->ch_endpt_count = 0;
2255
2256 ret = 0;
2257 } else {
2258 for (i = 0; i < client->ch_endpt_count; ++i) {
2259 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2260 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2261 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002262
Michal Vasko4f921012016-10-20 14:07:45 +02002263 /* move last endpoint to the empty space */
2264 --client->ch_endpt_count;
2265 if (i < client->ch_endpt_count) {
2266 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2267 } else if (!server_opts.ch_client_count) {
2268 free(server_opts.ch_clients);
2269 server_opts.ch_clients = NULL;
2270 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002271
Michal Vasko4f921012016-10-20 14:07:45 +02002272 ret = 0;
2273 break;
2274 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002275 }
2276 }
2277
2278 /* UNLOCK */
2279 nc_server_ch_client_unlock(client);
2280
2281 return ret;
2282}
2283
2284API int
2285nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2286{
2287 uint16_t i;
2288 int ret = -1;
2289 struct nc_ch_client *client;
2290
2291 if (!client_name) {
2292 ERRARG("client_name");
2293 return -1;
2294 } else if (!endpt_name) {
2295 ERRARG("endpt_name");
2296 return -1;
2297 } else if (!address) {
2298 ERRARG("address");
2299 return -1;
2300 }
2301
2302 /* LOCK */
2303 client = nc_server_ch_client_lock(client_name, 0, NULL);
2304 if (!client) {
2305 return -1;
2306 }
2307
2308 for (i = 0; i < client->ch_endpt_count; ++i) {
2309 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2310 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2311 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2312
2313 ret = 0;
2314 break;
2315 }
2316 }
2317
2318 /* UNLOCK */
2319 nc_server_ch_client_unlock(client);
2320
2321 if (ret == -1) {
2322 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2323 }
2324
2325 return ret;
2326}
2327
2328API int
2329nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2330{
2331 uint16_t i;
2332 int ret = -1;
2333 struct nc_ch_client *client;
2334
2335 if (!client_name) {
2336 ERRARG("client_name");
2337 return -1;
2338 } else if (!endpt_name) {
2339 ERRARG("endpt_name");
2340 return -1;
2341 } else if (!port) {
2342 ERRARG("port");
2343 return -1;
2344 }
2345
2346 /* LOCK */
2347 client = nc_server_ch_client_lock(client_name, 0, NULL);
2348 if (!client) {
2349 return -1;
2350 }
2351
2352 for (i = 0; i < client->ch_endpt_count; ++i) {
2353 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2354 client->ch_endpts[i].port = port;
2355
2356 ret = 0;
2357 break;
2358 }
2359 }
2360
2361 /* UNLOCK */
2362 nc_server_ch_client_unlock(client);
2363
2364 if (ret == -1) {
2365 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2366 }
2367
2368 return ret;
2369}
2370
2371API int
2372nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2373{
2374 struct nc_ch_client *client;
2375
2376 if (!client_name) {
2377 ERRARG("client_name");
2378 return -1;
2379 } else if (!conn_type) {
2380 ERRARG("conn_type");
2381 return -1;
2382 }
2383
2384 /* LOCK */
2385 client = nc_server_ch_client_lock(client_name, 0, NULL);
2386 if (!client) {
2387 return -1;
2388 }
2389
2390 if (client->conn_type != conn_type) {
2391 client->conn_type = conn_type;
2392
2393 /* set default options */
2394 switch (conn_type) {
2395 case NC_CH_PERSIST:
2396 client->conn.persist.idle_timeout = 86400;
2397 client->conn.persist.ka_max_wait = 30;
2398 client->conn.persist.ka_max_attempts = 3;
2399 break;
2400 case NC_CH_PERIOD:
2401 client->conn.period.idle_timeout = 300;
2402 client->conn.period.reconnect_timeout = 60;
2403 break;
2404 default:
2405 ERRINT;
2406 break;
2407 }
2408 }
2409
2410 /* UNLOCK */
2411 nc_server_ch_client_unlock(client);
2412
2413 return 0;
2414}
2415
2416API int
2417nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2418{
2419 struct nc_ch_client *client;
2420
2421 if (!client_name) {
2422 ERRARG("client_name");
2423 return -1;
2424 }
2425
2426 /* LOCK */
2427 client = nc_server_ch_client_lock(client_name, 0, NULL);
2428 if (!client) {
2429 return -1;
2430 }
2431
2432 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002433 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002434 /* UNLOCK */
2435 nc_server_ch_client_unlock(client);
2436 return -1;
2437 }
2438
2439 client->conn.persist.idle_timeout = idle_timeout;
2440
2441 /* UNLOCK */
2442 nc_server_ch_client_unlock(client);
2443
2444 return 0;
2445}
2446
2447API int
2448nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2449{
2450 struct nc_ch_client *client;
2451
2452 if (!client_name) {
2453 ERRARG("client_name");
2454 return -1;
2455 } else if (!max_wait) {
2456 ERRARG("max_wait");
2457 return -1;
2458 }
2459
2460 /* LOCK */
2461 client = nc_server_ch_client_lock(client_name, 0, NULL);
2462 if (!client) {
2463 return -1;
2464 }
2465
2466 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002467 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002468 /* UNLOCK */
2469 nc_server_ch_client_unlock(client);
2470 return -1;
2471 }
2472
2473 client->conn.persist.ka_max_wait = max_wait;
2474
2475 /* UNLOCK */
2476 nc_server_ch_client_unlock(client);
2477
2478 return 0;
2479}
2480
2481API int
2482nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2483{
2484 struct nc_ch_client *client;
2485
2486 if (!client_name) {
2487 ERRARG("client_name");
2488 return -1;
2489 }
2490
2491 /* LOCK */
2492 client = nc_server_ch_client_lock(client_name, 0, NULL);
2493 if (!client) {
2494 return -1;
2495 }
2496
2497 if (client->conn_type != NC_CH_PERSIST) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002498 ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002499 /* UNLOCK */
2500 nc_server_ch_client_unlock(client);
2501 return -1;
2502 }
2503
2504 client->conn.persist.ka_max_attempts = max_attempts;
2505
2506 /* UNLOCK */
2507 nc_server_ch_client_unlock(client);
2508
2509 return 0;
2510}
2511
2512API int
2513nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2514{
2515 struct nc_ch_client *client;
2516
2517 if (!client_name) {
2518 ERRARG("client_name");
2519 return -1;
2520 }
2521
2522 /* LOCK */
2523 client = nc_server_ch_client_lock(client_name, 0, NULL);
2524 if (!client) {
2525 return -1;
2526 }
2527
2528 if (client->conn_type != NC_CH_PERIOD) {
Darshanajk2d2bf4d2017-09-15 21:34:47 +10002529 ERR("Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002530 /* UNLOCK */
2531 nc_server_ch_client_unlock(client);
2532 return -1;
2533 }
2534
2535 client->conn.period.idle_timeout = idle_timeout;
2536
2537 /* UNLOCK */
2538 nc_server_ch_client_unlock(client);
2539
2540 return 0;
2541}
2542
2543API int
2544nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2545{
2546 struct nc_ch_client *client;
2547
2548 if (!client_name) {
2549 ERRARG("client_name");
2550 return -1;
2551 } else if (!reconnect_timeout) {
2552 ERRARG("reconnect_timeout");
2553 return -1;
2554 }
2555
2556 /* LOCK */
2557 client = nc_server_ch_client_lock(client_name, 0, NULL);
2558 if (!client) {
2559 return -1;
2560 }
2561
2562 if (client->conn_type != NC_CH_PERIOD) {
2563 ERR("Call Home client \"%s\" is not of periodic connection type.");
2564 /* UNLOCK */
2565 nc_server_ch_client_unlock(client);
2566 return -1;
2567 }
2568
2569 client->conn.period.reconnect_timeout = reconnect_timeout;
2570
2571 /* UNLOCK */
2572 nc_server_ch_client_unlock(client);
2573
2574 return 0;
2575}
2576
2577API int
2578nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2579{
2580 struct nc_ch_client *client;
2581
2582 if (!client_name) {
2583 ERRARG("client_name");
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->start_with = start_with;
2594
2595 /* UNLOCK */
2596 nc_server_ch_client_unlock(client);
2597
2598 return 0;
2599}
2600
2601API int
2602nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2603{
2604 struct nc_ch_client *client;
2605
2606 if (!client_name) {
2607 ERRARG("client_name");
2608 return -1;
2609 } else if (!max_attempts) {
2610 ERRARG("max_attempts");
2611 return -1;
2612 }
2613
2614 /* LOCK */
2615 client = nc_server_ch_client_lock(client_name, 0, NULL);
2616 if (!client) {
2617 return -1;
2618 }
2619
2620 client->max_attempts = max_attempts;
2621
2622 /* UNLOCK */
2623 nc_server_ch_client_unlock(client);
2624
2625 return 0;
2626}
2627
2628/* client lock is expected to be held */
2629static NC_MSG_TYPE
2630nc_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 +01002631{
Michal Vasko71090fc2016-05-24 16:37:28 +02002632 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002633 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02002634 struct timespec ts_cur;
Michal Vaskob05053d2016-01-22 16:12:06 +01002635
Michal Vasko2e6defd2016-10-07 15:48:15 +02002636 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002637 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002638 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002639 }
2640
Michal Vaskoade892d2017-02-22 13:40:35 +01002641 *session = nc_new_session(0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002642 if (!(*session)) {
2643 ERRMEM;
2644 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002645 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002646 }
2647 (*session)->status = NC_STATUS_STARTING;
2648 (*session)->side = NC_SERVER;
2649 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002650 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002651 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2652 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002653
2654 /* transport lock */
Michal Vaskob05053d2016-01-22 16:12:06 +01002655 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01002656 pthread_cond_init((*session)->ti_cond, NULL);
2657 *(*session)->ti_inuse = 0;
Michal Vaskob05053d2016-01-22 16:12:06 +01002658
2659 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002660#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002661 if (client->ti == NC_TI_LIBSSH) {
2662 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002663 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002664 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002665
Michal Vasko71090fc2016-05-24 16:37:28 +02002666 if (ret < 0) {
2667 msgtype = NC_MSG_ERROR;
2668 goto fail;
2669 } else if (!ret) {
2670 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002671 goto fail;
2672 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002673 } else
2674#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002675#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002676 if (client->ti == NC_TI_OPENSSL) {
2677 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002678 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002679 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002680
Michal Vasko71090fc2016-05-24 16:37:28 +02002681 if (ret < 0) {
2682 msgtype = NC_MSG_ERROR;
2683 goto fail;
2684 } else if (!ret) {
2685 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002686 goto fail;
2687 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002688 } else
2689#endif
2690 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002691 ERRINT;
2692 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002693 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002694 goto fail;
2695 }
2696
2697 /* assign new SID atomically */
2698 /* LOCK */
2699 pthread_spin_lock(&server_opts.sid_lock);
2700 (*session)->id = server_opts.new_session_id++;
2701 /* UNLOCK */
2702 pthread_spin_unlock(&server_opts.sid_lock);
2703
2704 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002705 msgtype = nc_handshake(*session);
2706 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002707 goto fail;
2708 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002709
2710 nc_gettimespec_mono(&ts_cur);
2711 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2712 nc_gettimespec_real(&ts_cur);
2713 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01002714 (*session)->status = NC_STATUS_RUNNING;
2715
Michal Vasko71090fc2016-05-24 16:37:28 +02002716 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002717
2718fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002719 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002720 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002721 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002722}
2723
Michal Vasko2e6defd2016-10-07 15:48:15 +02002724struct nc_ch_client_thread_arg {
2725 char *client_name;
2726 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2727};
2728
2729static struct nc_ch_client *
2730nc_server_ch_client_with_endpt_lock(const char *name)
2731{
2732 struct nc_ch_client *client;
2733
2734 while (1) {
2735 /* LOCK */
2736 client = nc_server_ch_client_lock(name, 0, NULL);
2737 if (!client) {
2738 return NULL;
2739 }
2740 if (client->ch_endpt_count) {
2741 return client;
2742 }
2743 /* no endpoints defined yet */
2744
2745 /* UNLOCK */
2746 nc_server_ch_client_unlock(client);
2747
2748 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2749 }
2750
2751 return NULL;
2752}
2753
2754static int
2755nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2756{
2757 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002758 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002759 struct timespec ts;
2760 struct nc_ch_client *client;
2761
2762 /* session created, initialize condition */
2763 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2764 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2765 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2766 ERRMEM;
2767 nc_session_free(session, NULL);
2768 return -1;
2769 }
2770 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2771 pthread_cond_init(session->opts.server.ch_cond, NULL);
2772
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002773 session->flags |= NC_SESSION_CALLHOME;
2774
Michal Vasko2e6defd2016-10-07 15:48:15 +02002775 /* CH LOCK */
2776 pthread_mutex_lock(session->opts.server.ch_lock);
2777
2778 /* give the session to the user */
2779 data->session_clb(data->client_name, session);
2780
2781 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02002782 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01002783 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002784
2785 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2786 if (ret && (ret != ETIMEDOUT)) {
2787 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2788 goto ch_client_remove;
2789 }
2790
2791 /* check whether the client was not removed */
2792 /* LOCK */
2793 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2794 if (!client) {
2795 /* client was removed, finish thread */
2796 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2797 data->client_name);
2798 goto ch_client_remove;
2799 }
2800
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002801 if (client->conn_type == NC_CH_PERSIST) {
2802 /* TODO keep-alives */
2803 idle_timeout = client->conn.persist.idle_timeout;
2804 } else {
2805 idle_timeout = client->conn.period.idle_timeout;
2806 }
2807
Michal Vasko9fb42272017-10-05 13:50:05 +02002808 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01002809 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 +02002810 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2811 session->status = NC_STATUS_INVALID;
2812 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2813 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002814
2815 /* UNLOCK */
2816 nc_server_ch_client_unlock(client);
2817
2818 } while (session->status == NC_STATUS_RUNNING);
2819
2820 /* CH UNLOCK */
2821 pthread_mutex_unlock(session->opts.server.ch_lock);
2822
2823 return 0;
2824
2825ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002826 /* make the session a standard one */
2827 pthread_cond_destroy(session->opts.server.ch_cond);
2828 free(session->opts.server.ch_cond);
2829 session->opts.server.ch_cond = NULL;
2830
2831 session->flags &= ~NC_SESSION_CALLHOME;
2832
Michal Vasko2e6defd2016-10-07 15:48:15 +02002833 /* CH UNLOCK */
2834 pthread_mutex_unlock(session->opts.server.ch_lock);
2835
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002836 pthread_mutex_destroy(session->opts.server.ch_lock);
2837 free(session->opts.server.ch_lock);
2838 session->opts.server.ch_lock = NULL;
2839
Michal Vasko2e6defd2016-10-07 15:48:15 +02002840 return 1;
2841}
2842
2843static void *
2844nc_ch_client_thread(void *arg)
2845{
2846 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2847 NC_MSG_TYPE msgtype;
2848 uint8_t cur_attempts = 0;
2849 uint16_t i;
Michal Vasko9550cf12017-03-21 15:33:58 +01002850 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002851 struct nc_ch_endpt *cur_endpt;
2852 struct nc_session *session;
2853 struct nc_ch_client *client;
2854
2855 /* LOCK */
2856 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2857 if (!client) {
2858 goto cleanup;
2859 }
2860
2861 cur_endpt = &client->ch_endpts[0];
2862 cur_endpt_name = strdup(cur_endpt->name);
2863
Michal Vasko29af44b2016-10-13 10:59:55 +02002864 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002865 while (1) {
2866 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2867
2868 if (msgtype == NC_MSG_HELLO) {
2869 /* UNLOCK */
2870 nc_server_ch_client_unlock(client);
2871
Michal Vasko29af44b2016-10-13 10:59:55 +02002872 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002873 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2874 goto cleanup;
2875 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002876 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002877
2878 /* LOCK */
2879 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2880 if (!client) {
2881 goto cleanup;
2882 }
2883
2884 /* session changed status -> it was disconnected for whatever reason,
2885 * persistent connection immediately tries to reconnect, periodic waits some first */
2886 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002887 /* UNLOCK */
2888 nc_server_ch_client_unlock(client);
2889
2890 /* TODO wake up sometimes to check for new notifications */
2891 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2892
2893 /* LOCK */
2894 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2895 if (!client) {
2896 goto cleanup;
2897 }
2898 }
2899
2900 /* set next endpoint to try */
2901 if (client->start_with == NC_CH_FIRST_LISTED) {
2902 cur_endpt = &client->ch_endpts[0];
2903 free(cur_endpt_name);
2904 cur_endpt_name = strdup(cur_endpt->name);
2905 } /* else we keep the current one */
2906 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02002907 /* UNLOCK */
2908 nc_server_ch_client_unlock(client);
2909
Michal Vasko2e6defd2016-10-07 15:48:15 +02002910 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002911 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2912
Michal Vasko6bb116b2016-10-26 13:53:46 +02002913 /* LOCK */
2914 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2915 if (!client) {
2916 goto cleanup;
2917 }
2918
Michal Vasko2e6defd2016-10-07 15:48:15 +02002919 ++cur_attempts;
2920 if (cur_attempts == client->max_attempts) {
2921 for (i = 0; i < client->ch_endpt_count; ++i) {
2922 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2923 break;
2924 }
2925 }
2926 if (i < client->ch_endpt_count - 1) {
2927 /* just go to the next endpoint */
2928 cur_endpt = &client->ch_endpts[i + 1];
2929 free(cur_endpt_name);
2930 cur_endpt_name = strdup(cur_endpt->name);
2931 } else {
2932 /* cur_endpoint was removed or is the last, either way start with the first one */
2933 cur_endpt = &client->ch_endpts[0];
2934 free(cur_endpt_name);
2935 cur_endpt_name = strdup(cur_endpt->name);
2936 }
2937
2938 cur_attempts = 0;
2939 } /* else we keep the current one */
2940 }
2941 }
2942
2943cleanup:
2944 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01002945 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002946 free(data->client_name);
2947 free(data);
2948 return NULL;
2949}
2950
2951API int
2952nc_connect_ch_client_dispatch(const char *client_name,
2953 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2954 int ret;
2955 pthread_t tid;
2956 struct nc_ch_client_thread_arg *arg;
2957
2958 if (!client_name) {
2959 ERRARG("client_name");
2960 return -1;
2961 } else if (!session_clb) {
2962 ERRARG("session_clb");
2963 return -1;
2964 }
2965
2966 arg = malloc(sizeof *arg);
2967 if (!arg) {
2968 ERRMEM;
2969 return -1;
2970 }
2971 arg->client_name = strdup(client_name);
2972 if (!arg->client_name) {
2973 ERRMEM;
2974 free(arg);
2975 return -1;
2976 }
2977 arg->session_clb = session_clb;
2978
2979 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2980 if (ret) {
2981 ERR("Creating a new thread failed (%s).", strerror(ret));
2982 free(arg->client_name);
2983 free(arg);
2984 return -1;
2985 }
2986 /* the thread now manages arg */
2987
2988 pthread_detach(tid);
2989
2990 return 0;
2991}
2992
Radek Krejci53691be2016-02-22 13:58:37 +01002993#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002994
Michal Vaskoe8e07702017-03-15 10:19:30 +01002995API int
2996nc_server_endpt_count(void)
2997{
2998 return server_opts.endpt_count;
2999}
3000
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003001API time_t
3002nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003003{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003004 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003005 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003006 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003007 }
3008
Michal Vasko2e6defd2016-10-07 15:48:15 +02003009 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003010}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003011
3012API void
3013nc_session_set_notif_status(struct nc_session *session, int notif_status)
3014{
3015 if (!session || (session->side != NC_SERVER)) {
3016 ERRARG("session");
3017 return;
3018 }
3019
3020 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3021}
3022
3023API int
3024nc_session_get_notif_status(const struct nc_session *session)
3025{
3026 if (!session || (session->side != NC_SERVER)) {
3027 ERRARG("session");
3028 return 0;
3029 }
3030
3031 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003032}