blob: 30bb18c0370389a86170ea4c6cdb15743895e4ba [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 */
408 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
409 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200410 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
411 }
412 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100413 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
414 nc_err_set_msg(err, "The requested schema was not found.", "en");
415 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100416 }
417
418 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100419 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100420 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
Radek Krejci90fba642016-12-07 15:59:45 +0100421 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100422 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
423 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100424 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
425 nc_err_set_msg(err, "The requested format is not supported.", "en");
426 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100427 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200428 if (!model_data) {
429 ERRINT;
430 return NULL;
431 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100432
Michal Vasko88639e92017-08-03 14:38:10 +0200433 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/data", 1);
434 if (!sdata) {
435 ERRINT;
436 free(model_data);
437 return NULL;
438 }
439
Radek Krejci539efb62016-08-24 15:05:16 +0200440 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
441 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko3cb0b132017-01-03 14:59:51 +0100442 if (!data || lyd_validate(&data, LYD_OPT_RPCREPLY, NULL)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100443 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200444 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100445 return NULL;
446 }
447
Radek Krejci36dfdb32016-09-01 16:56:35 +0200448 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100449}
450
451static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100452nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100453{
Michal Vasko428087d2016-01-14 16:04:28 +0100454 session->term_reason = NC_SESSION_TERM_CLOSED;
455 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100456}
457
Michal Vasko086311b2016-01-08 09:53:11 +0100458API int
459nc_server_init(struct ly_ctx *ctx)
460{
Michal Vasko88639e92017-08-03 14:38:10 +0200461 const struct lys_node *rpc;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100462
Michal Vasko086311b2016-01-08 09:53:11 +0100463 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200464 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100465 return -1;
466 }
467
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100468 nc_init();
469
Michal Vasko05ba9df2016-01-13 14:40:27 +0100470 /* set default <get-schema> callback if not specified */
Michal Vasko88639e92017-08-03 14:38:10 +0200471 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
472 if (rpc && !rpc->priv) {
473 lys_set_private(rpc, nc_clb_default_get_schema);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100474 }
475
476 /* set default <close-session> callback if not specififed */
Michal Vasko88639e92017-08-03 14:38:10 +0200477 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session", 0);
478 if (rpc && !rpc->priv) {
479 lys_set_private(rpc, nc_clb_default_close_session);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100480 }
481
Michal Vasko086311b2016-01-08 09:53:11 +0100482 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100483
484 server_opts.new_session_id = 1;
485 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
486
Michal Vasko086311b2016-01-08 09:53:11 +0100487 return 0;
488}
489
Michal Vaskob48aa812016-01-18 14:13:09 +0100490API void
491nc_server_destroy(void)
492{
Radek Krejci658782b2016-12-04 22:04:55 +0100493 unsigned int i;
494
495 for (i = 0; i < server_opts.capabilities_count; i++) {
496 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
497 }
498 free(server_opts.capabilities);
Michal Vaskob48aa812016-01-18 14:13:09 +0100499 pthread_spin_destroy(&server_opts.sid_lock);
500
Radek Krejci53691be2016-02-22 13:58:37 +0100501#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100502 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100503#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100504#ifdef NC_ENABLED_SSH
505 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100506
507 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
508 server_opts.hostkey_data_free(server_opts.hostkey_data);
509 }
510#endif
511#ifdef NC_ENABLED_TLS
512 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
513 server_opts.server_cert_data_free(server_opts.server_cert_data);
514 }
515 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
516 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
517 }
Michal Vaskob48aa812016-01-18 14:13:09 +0100518#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100519 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100520}
521
Michal Vasko086311b2016-01-08 09:53:11 +0100522API int
523nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
524{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200525 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
526 ERRARG("basic_mode");
527 return -1;
528 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
529 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100530 return -1;
531 }
532
533 server_opts.wd_basic_mode = basic_mode;
534 server_opts.wd_also_supported = also_supported;
535 return 0;
536}
537
Michal Vasko1a38c862016-01-15 15:50:07 +0100538API void
Michal Vasko55f03972016-04-13 08:56:01 +0200539nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
540{
541 if (!basic_mode && !also_supported) {
542 ERRARG("basic_mode and also_supported");
543 return;
544 }
545
546 if (basic_mode) {
547 *basic_mode = server_opts.wd_basic_mode;
548 }
549 if (also_supported) {
550 *also_supported = server_opts.wd_also_supported;
551 }
552}
553
Michal Vasko55f03972016-04-13 08:56:01 +0200554API int
Radek Krejci658782b2016-12-04 22:04:55 +0100555nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200556{
Radek Krejci658782b2016-12-04 22:04:55 +0100557 const char **new;
558
559 if (!value || !value[0]) {
560 ERRARG("value must not be empty");
561 return EXIT_FAILURE;
562 }
563
564 server_opts.capabilities_count++;
565 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
566 if (!new) {
567 ERRMEM;
568 return EXIT_FAILURE;
569 }
570 server_opts.capabilities = new;
571 server_opts.capabilities[server_opts.capabilities_count - 1] = lydict_insert(server_opts.ctx, value, 0);
572
573 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200574}
575
Michal Vasko1a38c862016-01-15 15:50:07 +0100576API void
Michal Vasko086311b2016-01-08 09:53:11 +0100577nc_server_set_hello_timeout(uint16_t hello_timeout)
578{
Michal Vasko086311b2016-01-08 09:53:11 +0100579 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100580}
581
Michal Vasko55f03972016-04-13 08:56:01 +0200582API uint16_t
583nc_server_get_hello_timeout(void)
584{
585 return server_opts.hello_timeout;
586}
587
Michal Vasko1a38c862016-01-15 15:50:07 +0100588API void
Michal Vasko086311b2016-01-08 09:53:11 +0100589nc_server_set_idle_timeout(uint16_t idle_timeout)
590{
Michal Vasko086311b2016-01-08 09:53:11 +0100591 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100592}
593
Michal Vasko55f03972016-04-13 08:56:01 +0200594API uint16_t
595nc_server_get_idle_timeout(void)
596{
597 return server_opts.idle_timeout;
598}
599
Michal Vasko71090fc2016-05-24 16:37:28 +0200600API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100601nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100602{
Michal Vasko71090fc2016-05-24 16:37:28 +0200603 NC_MSG_TYPE msgtype;
604
Michal Vasko45e53ae2016-04-07 11:46:03 +0200605 if (!server_opts.ctx) {
606 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200607 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200608 } else if (fdin < 0) {
609 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200610 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200611 } else if (fdout < 0) {
612 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200613 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200614 } else if (!username) {
615 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200616 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200617 } else if (!session) {
618 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200619 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100620 }
621
622 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +0100623 *session = nc_new_session(0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100624 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100625 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200626 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100627 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100628 (*session)->status = NC_STATUS_STARTING;
629 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100630
Michal Vaskoade892d2017-02-22 13:40:35 +0100631 /* transport lock */
632 pthread_mutex_init((*session)->ti_lock, NULL);
633 pthread_cond_init((*session)->ti_cond, NULL);
634 *(*session)->ti_inuse = 0;
635
Michal Vasko086311b2016-01-08 09:53:11 +0100636 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100637 (*session)->ti_type = NC_TI_FD;
638 (*session)->ti.fd.in = fdin;
639 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100640
641 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100642 (*session)->flags = NC_SESSION_SHAREDCTX;
643 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100644
Michal Vaskob48aa812016-01-18 14:13:09 +0100645 /* assign new SID atomically */
646 pthread_spin_lock(&server_opts.sid_lock);
647 (*session)->id = server_opts.new_session_id++;
648 pthread_spin_unlock(&server_opts.sid_lock);
649
Michal Vasko086311b2016-01-08 09:53:11 +0100650 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200651 msgtype = nc_handshake(*session);
652 if (msgtype != NC_MSG_HELLO) {
653 nc_session_free(*session, NULL);
654 *session = NULL;
655 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100656 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200657 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100658 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100659
Michal Vasko71090fc2016-05-24 16:37:28 +0200660 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100661}
Michal Vasko9e036d52016-01-08 10:49:26 +0100662
Michal Vaskob30b99c2016-07-26 11:35:43 +0200663static void
664nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
665{
666 uint8_t i, found = 0;
667
668 for (i = 0; i < ps->queue_len; ++i) {
669 /* idx round buffer adjust */
670 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
671 i = -ps->queue_begin;
672 }
673
674 if (found) {
675 /* move the value back one place */
676 if (ps->queue[ps->queue_begin + i] == id) {
677 /* another equal value, simply cannot be */
678 ERRINT;
679 }
680
681 if (ps->queue_begin + i == 0) {
682 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
683 } else {
684 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
685 }
686 } else if (ps->queue[ps->queue_begin + i] == id) {
687 /* found our id, there can be no more equal valid values */
688 found = 1;
689 }
690 }
691
692 if (!found) {
693 ERRINT;
694 }
695 --ps->queue_len;
696}
697
Michal Vaskof04a52a2016-04-07 10:52:10 +0200698int
Michal Vasko26043172016-07-26 14:08:59 +0200699nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200700{
701 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200702 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200703 struct timespec ts;
704
Radek Krejci7ac16052016-07-15 11:48:18 +0200705 nc_gettimespec(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100706 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200707
708 /* LOCK */
709 ret = pthread_mutex_timedlock(&ps->lock, &ts);
710 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200711 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200712 return -1;
713 }
714
715 /* get a unique queue value (by adding 1 to the last added value, if any) */
716 if (ps->queue_len) {
717 queue_last = ps->queue_begin + ps->queue_len - 1;
718 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
719 queue_last -= NC_PS_QUEUE_SIZE;
720 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200721 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200722 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200723 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200724 }
725
726 /* add ourselves into the queue */
727 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200728 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200729 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200730 return -1;
731 }
732 ++ps->queue_len;
733 queue_last = ps->queue_begin + ps->queue_len - 1;
734 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
735 queue_last -= NC_PS_QUEUE_SIZE;
736 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200737 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200738
739 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200740 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200741 nc_gettimespec(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100742 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200743
744 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
745 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200746 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200747 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200748 nc_ps_queue_remove_id(ps, *id);
749 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200750 return -1;
751 }
752 }
753
Michal Vaskobe86fe32016-04-07 10:43:03 +0200754 /* UNLOCK */
755 pthread_mutex_unlock(&ps->lock);
756
757 return 0;
758}
759
Michal Vaskof04a52a2016-04-07 10:52:10 +0200760int
Michal Vasko26043172016-07-26 14:08:59 +0200761nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200762{
763 int ret;
764 struct timespec ts;
765
Radek Krejci7ac16052016-07-15 11:48:18 +0200766 nc_gettimespec(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100767 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200768
769 /* LOCK */
770 ret = pthread_mutex_timedlock(&ps->lock, &ts);
771 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200772 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200773 ret = -1;
774 }
775
Michal Vaskob30b99c2016-07-26 11:35:43 +0200776 /* we must be the first, it was our turn after all, right? */
777 if (ps->queue[ps->queue_begin] != id) {
778 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200779 /* UNLOCK */
780 if (!ret) {
781 pthread_mutex_unlock(&ps->lock);
782 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200783 return -1;
784 }
785
Michal Vaskobe86fe32016-04-07 10:43:03 +0200786 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200787 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200788
789 /* broadcast to all other threads that the queue moved */
790 pthread_cond_broadcast(&ps->cond);
791
Michal Vaskobe86fe32016-04-07 10:43:03 +0200792 /* UNLOCK */
793 if (!ret) {
794 pthread_mutex_unlock(&ps->lock);
795 }
796
797 return ret;
798}
799
Michal Vasko428087d2016-01-14 16:04:28 +0100800API struct nc_pollsession *
801nc_ps_new(void)
802{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100803 struct nc_pollsession *ps;
804
805 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100806 if (!ps) {
807 ERRMEM;
808 return NULL;
809 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200810 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100811 pthread_mutex_init(&ps->lock, NULL);
812
813 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100814}
815
816API void
817nc_ps_free(struct nc_pollsession *ps)
818{
fanchanghu3d4e7212017-08-09 09:42:30 +0800819 uint16_t i;
820
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100821 if (!ps) {
822 return;
823 }
824
Michal Vaskobe86fe32016-04-07 10:43:03 +0200825 if (ps->queue_len) {
826 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
827 }
828
fanchanghu3d4e7212017-08-09 09:42:30 +0800829 for (i = 0; i < ps->session_count; i++) {
830 free(ps->sessions[i]);
831 }
832
Michal Vasko428087d2016-01-14 16:04:28 +0100833 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100834 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200835 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100836
Michal Vasko428087d2016-01-14 16:04:28 +0100837 free(ps);
838}
839
840API int
841nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
842{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200843 uint8_t q_id;
844
Michal Vasko45e53ae2016-04-07 11:46:03 +0200845 if (!ps) {
846 ERRARG("ps");
847 return -1;
848 } else if (!session) {
849 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100850 return -1;
851 }
852
Michal Vasko48a63ed2016-03-01 09:48:21 +0100853 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200854 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200855 return -1;
856 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100857
Michal Vasko428087d2016-01-14 16:04:28 +0100858 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100859 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +0100860 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100861 ERRMEM;
862 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200863 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100864 return -1;
865 }
fanchanghu3d4e7212017-08-09 09:42:30 +0800866 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
867 if (!ps->sessions[ps->session_count - 1]) {
868 ERRMEM;
869 --ps->session_count;
870 /* UNLOCK */
871 nc_ps_unlock(ps, q_id, __func__);
872 return -1;
873 }
874 ps->sessions[ps->session_count - 1]->session = session;
875 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +0100876
Michal Vasko48a63ed2016-03-01 09:48:21 +0100877 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200878 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100879}
880
Michal Vasko48a63ed2016-03-01 09:48:21 +0100881static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100882_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100883{
884 uint16_t i;
885
Radek Krejcid5f978f2016-03-03 13:14:45 +0100886 if (index >= 0) {
887 i = (uint16_t)index;
888 goto remove;
889 }
Michal Vasko428087d2016-01-14 16:04:28 +0100890 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +0800891 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100892remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100893 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +0800894 if (i <= ps->session_count) {
895 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +0100896 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +0800897 }
898 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +0100899 free(ps->sessions);
900 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +0100901 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +0100902 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100903 return 0;
904 }
905 }
906
Michal Vaskof0537d82016-01-29 14:42:38 +0100907 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100908}
909
Michal Vasko48a63ed2016-03-01 09:48:21 +0100910API int
911nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
912{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200913 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200914 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100915
Michal Vasko45e53ae2016-04-07 11:46:03 +0200916 if (!ps) {
917 ERRARG("ps");
918 return -1;
919 } else if (!session) {
920 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100921 return -1;
922 }
923
924 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200925 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200926 return -1;
927 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100928
Radek Krejcid5f978f2016-03-03 13:14:45 +0100929 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100930
931 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200932 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100933
Michal Vaskobe86fe32016-04-07 10:43:03 +0200934 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100935}
936
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100937API struct nc_session *
938nc_ps_get_session_by_sid(const struct nc_pollsession *ps, uint32_t sid)
939{
940 uint8_t q_id;
941 uint16_t i;
942 struct nc_session *ret = NULL;
943
944 if (!ps) {
945 ERRARG("ps");
946 return NULL;
947 }
948
949 /* LOCK */
950 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
951 return NULL;
952 }
953
954 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +0800955 if (ps->sessions[i]->session->id == sid) {
956 ret = ps->sessions[i]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +0100957 break;
958 }
959 }
960
961 /* UNLOCK */
962 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
963
964 return ret;
965}
966
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100967API uint16_t
968nc_ps_session_count(struct nc_pollsession *ps)
969{
970 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200971 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100972 return 0;
973 }
974
Michal Vaskof4462fd2017-02-15 14:29:05 +0100975 return ps->session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100976}
977
Michal Vasko71090fc2016-05-24 16:37:28 +0200978/* must be called holding the session lock!
979 * returns: NC_PSPOLL_ERROR,
980 * NC_PSPOLL_BAD_RPC,
981 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
982 * NC_PSPOLL_RPC
983 */
984static int
Radek Krejci93e80222016-10-03 13:34:25 +0200985nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100986{
987 struct lyxml_elem *xml = NULL;
988 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200989 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200990 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100991
Michal Vasko45e53ae2016-04-07 11:46:03 +0200992 if (!session) {
993 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200994 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200995 } else if (!rpc) {
996 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200997 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100998 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100999 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001000 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001001 }
1002
1003 msgtype = nc_read_msg(session, &xml);
1004
1005 switch (msgtype) {
1006 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +02001007 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001008 if (!*rpc) {
1009 ERRMEM;
1010 goto error;
1011 }
Michal Vaskoca4a2422016-02-02 12:17:14 +01001012
Radek Krejcif93c7d42016-04-06 13:41:15 +02001013 ly_errno = LY_SUCCESS;
Michal Vasko41adf392017-01-17 10:39:04 +01001014 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child,
1015 LYD_OPT_RPC | LYD_OPT_DESTRUCT | LYD_OPT_NOEXTDEPS | LYD_OPT_STRICT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001016 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +02001017 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +02001018 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +02001019 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001020 nc_server_reply_free(reply);
1021 if (ret == -1) {
1022 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +02001023 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001024 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1025 } else {
1026 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +01001027 }
Michal Vasko428087d2016-01-14 16:04:28 +01001028 (*rpc)->root = xml;
1029 break;
1030 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +01001031 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001032 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001033 goto error;
1034 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +01001035 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001036 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001037 goto error;
1038 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +01001039 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001040 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +01001041 goto error;
1042 default:
Michal Vasko71090fc2016-05-24 16:37:28 +02001043 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +01001044 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
1045 */
Michal Vasko71090fc2016-05-24 16:37:28 +02001046 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001047 break;
1048 }
1049
Michal Vasko71090fc2016-05-24 16:37:28 +02001050 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001051
1052error:
1053 /* cleanup */
1054 lyxml_free(server_opts.ctx, xml);
1055
Michal Vasko71090fc2016-05-24 16:37:28 +02001056 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001057}
1058
fanchanghu966f2de2016-07-21 02:28:57 -04001059API void
1060nc_set_global_rpc_clb(nc_rpc_clb clb)
1061{
1062 global_rpc_clb = clb;
1063}
1064
Radek Krejci93e80222016-10-03 13:34:25 +02001065API NC_MSG_TYPE
1066nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1067{
1068 NC_MSG_TYPE result = NC_MSG_NOTIF;
1069 int ret;
1070
1071 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001072 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001073 ERRARG("session");
1074 return NC_MSG_ERROR;
1075 } else if (!notif || !notif->tree || !notif->eventtime) {
1076 ERRARG("notif");
1077 return NC_MSG_ERROR;
1078 }
1079
1080 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vaskoade892d2017-02-22 13:40:35 +01001081 ret = nc_session_lock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001082 if (ret < 0) {
1083 return NC_MSG_ERROR;
1084 } else if (!ret) {
1085 return NC_MSG_WOULDBLOCK;
1086 }
1087
1088 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1089 if (ret == -1) {
1090 ERR("Session %u: failed to write notification.", session->id);
1091 result = NC_MSG_ERROR;
1092 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001093
1094 nc_session_unlock(session, timeout, __func__);
Radek Krejci93e80222016-10-03 13:34:25 +02001095
1096 return result;
1097}
1098
Michal Vasko71090fc2016-05-24 16:37:28 +02001099/* must be called holding the session lock!
1100 * returns: NC_PSPOLL_ERROR,
1101 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1102 * NC_PSPOLL_REPLY_ERROR,
1103 * 0
1104 */
1105static int
Radek Krejci93e80222016-10-03 13:34:25 +02001106nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001107{
1108 nc_rpc_clb clb;
1109 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001110 struct lys_node *rpc_act = NULL;
1111 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001112 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001113
Michal Vasko4a827e52016-03-03 10:59:00 +01001114 if (!rpc) {
1115 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001116 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001117 }
1118
Michal Vasko90e8e692016-07-13 12:27:57 +02001119 if (rpc->tree->schema->nodetype == LYS_RPC) {
1120 /* RPC */
1121 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001122 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001123 /* action */
1124 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1125 if (elem->schema->nodetype == LYS_ACTION) {
1126 rpc_act = elem->schema;
1127 break;
1128 }
1129 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001130 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001131 if (!rpc_act) {
1132 ERRINT;
1133 return NC_PSPOLL_ERROR;
1134 }
1135 }
1136
1137 if (!rpc_act->priv) {
1138 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001139 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001140 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001141 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001142 reply = clb(rpc->tree, session);
1143 }
1144
1145 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001146 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001147 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001148 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1149 if (reply->type == NC_RPL_ERROR) {
1150 ret |= NC_PSPOLL_REPLY_ERROR;
1151 }
1152 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001153
Michal Vasko71090fc2016-05-24 16:37:28 +02001154 if (r == -1) {
1155 ERR("Session %u: failed to write reply.", session->id);
1156 ret |= NC_PSPOLL_ERROR;
1157 }
Michal Vasko428087d2016-01-14 16:04:28 +01001158
1159 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1160 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1161 session->status = NC_STATUS_INVALID;
1162 }
1163
Michal Vasko71090fc2016-05-24 16:37:28 +02001164 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001165}
1166
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001167/* session must be running and session lock held!
1168 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1169 * NC_PSPOLL_ERROR, (msg filled)
1170 * NC_PSPOLL_TIMEOUT,
1171 * NC_PSPOLL_RPC (some application data available),
1172 * NC_PSPOLL_SSH_CHANNEL,
1173 * NC_PSPOLL_SSH_MSG
1174 */
1175static int
1176nc_ps_poll_session(struct nc_session *session, time_t now, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001177{
Michal Vasko9a327362017-01-11 11:31:46 +01001178 struct pollfd pfd;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001179 int r, ret;
Michal Vasko9a327362017-01-11 11:31:46 +01001180#ifdef NC_ENABLED_SSH
1181 struct nc_session *new;
1182#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001183
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001184 /* check timeout first */
1185 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout
1186 && (now >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
1187 sprintf(msg, "session idle timeout elapsed");
1188 session->status = NC_STATUS_INVALID;
1189 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1190 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1191 }
1192
1193 switch (session->ti_type) {
1194#ifdef NC_ENABLED_SSH
1195 case NC_TI_LIBSSH:
1196 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1197 if (r < 1) {
1198 if (r == SSH_EOF) {
1199 sprintf(msg, "SSH channel unexpected EOF");
1200 session->status = NC_STATUS_INVALID;
1201 session->term_reason = NC_SESSION_TERM_DROPPED;
1202 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1203 } else if (r == SSH_ERROR) {
1204 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
1205 session->status = NC_STATUS_INVALID;
1206 session->term_reason = NC_SESSION_TERM_OTHER;
1207 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1208 } else {
1209 ret = NC_PSPOLL_TIMEOUT;
1210 }
1211 break;
1212 }
1213
1214 /* we have some data, but it may be just an SSH message */
1215 r = ssh_execute_message_callbacks(session->ti.libssh.session);
1216 if (r != SSH_OK) {
1217 sprintf(msg, "failed to receive SSH messages (%s)", ssh_get_error(session->ti.libssh.session));
1218 session->status = NC_STATUS_INVALID;
1219 session->term_reason = NC_SESSION_TERM_OTHER;
1220 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1221 } else if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1222 /* new SSH message */
1223 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1224 if (session->ti.libssh.next) {
1225 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1226 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1227 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1228 /* new NETCONF SSH channel */
1229 ret = NC_PSPOLL_SSH_CHANNEL;
1230 break;
1231 }
1232 }
1233 if (new != session) {
1234 break;
1235 }
1236 }
1237
1238 /* just some SSH message */
1239 ret = NC_PSPOLL_SSH_MSG;
1240 } else {
1241 /* we have some application data */
1242 ret = NC_PSPOLL_RPC;
1243 }
1244 break;
1245#endif
1246#ifdef NC_ENABLED_TLS
1247 case NC_TI_OPENSSL:
1248 r = SSL_pending(session->ti.tls);
1249 if (!r) {
1250 /* no data pending in the SSL buffer, poll fd */
1251 pfd.fd = SSL_get_rfd(session->ti.tls);
1252 if (pfd.fd < 0) {
1253 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1254 ret = NC_PSPOLL_ERROR;
1255 break;
1256 }
1257 pfd.events = POLLIN;
1258 pfd.revents = 0;
1259 r = poll(&pfd, 1, 0);
1260
1261 if ((r < 0) && (errno != EINTR)) {
1262 sprintf(msg, "poll failed (%s)", strerror(errno));
1263 session->status = NC_STATUS_INVALID;
1264 ret = NC_PSPOLL_ERROR;
1265 } else if (r > 0) {
1266 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1267 sprintf(msg, "communication socket unexpectedly closed");
1268 session->status = NC_STATUS_INVALID;
1269 session->term_reason = NC_SESSION_TERM_DROPPED;
1270 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1271 } else if (pfd.revents & POLLERR) {
1272 sprintf(msg, "communication socket error");
1273 session->status = NC_STATUS_INVALID;
1274 session->term_reason = NC_SESSION_TERM_OTHER;
1275 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1276 } else {
1277 ret = NC_PSPOLL_RPC;
1278 }
1279 } else {
1280 ret = NC_PSPOLL_TIMEOUT;
1281 }
1282 } else {
1283 ret = NC_PSPOLL_RPC;
1284 }
1285 break;
1286#endif
1287 case NC_TI_FD:
1288 pfd.fd = session->ti.fd.in;
1289 pfd.events = POLLIN;
1290 pfd.revents = 0;
1291 r = poll(&pfd, 1, 0);
1292
1293 if ((r < 0) && (errno != EINTR)) {
1294 sprintf(msg, "poll failed (%s)", strerror(errno));
1295 session->status = NC_STATUS_INVALID;
1296 ret = NC_PSPOLL_ERROR;
1297 } else if (r > 0) {
1298 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1299 sprintf(msg, "communication socket unexpectedly closed");
1300 session->status = NC_STATUS_INVALID;
1301 session->term_reason = NC_SESSION_TERM_DROPPED;
1302 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1303 } else if (pfd.revents & POLLERR) {
1304 sprintf(msg, "communication socket error");
1305 session->status = NC_STATUS_INVALID;
1306 session->term_reason = NC_SESSION_TERM_OTHER;
1307 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1308 } else {
1309 ret = NC_PSPOLL_RPC;
1310 }
1311 } else {
1312 ret = NC_PSPOLL_TIMEOUT;
1313 }
1314 break;
1315 case NC_TI_NONE:
1316 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1317 ret = NC_PSPOLL_ERROR;
1318 break;
1319 }
1320
1321 return ret;
1322}
1323
1324API int
1325nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1326{
1327 int ret, r;
1328 uint8_t q_id;
1329 uint16_t i, j;
1330 char msg[256];
1331 struct timespec ts_timeout, ts_cur;
1332 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001333 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001334 struct nc_server_rpc *rpc = NULL;
1335
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001336 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001337 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001338 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001339 }
1340
Michal Vaskoade892d2017-02-22 13:40:35 +01001341 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001342 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001343 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001344 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001345
Michal Vaskoade892d2017-02-22 13:40:35 +01001346 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001347 nc_ps_unlock(ps, q_id, __func__);
1348 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001349 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001350
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001351 /* fill timespecs */
Michal Vasko36c7be82017-02-22 13:37:59 +01001352 nc_gettimespec(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001353 if (timeout > -1) {
1354 nc_gettimespec(&ts_timeout);
1355 nc_addtimespec(&ts_timeout, timeout);
1356 }
1357
1358 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001359 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001360 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001361 if (ps->last_event_session == ps->session_count - 1) {
1362 i = j = 0;
1363 } else {
1364 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001365 }
Michal Vasko9a327362017-01-11 11:31:46 +01001366 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001367 cur_ps_session = ps->sessions[i];
1368 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001369
Michal Vaskoade892d2017-02-22 13:40:35 +01001370 /* SESSION LOCK */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001371 r = nc_session_lock(cur_session, 0, __func__);
1372 if (r == -1) {
1373 ret = NC_PSPOLL_ERROR;
1374 } else if (r == 1) {
1375 /* no one else is currently working with the session, so we can, otherwise skip it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001376 if (cur_ps_session->state == NC_PS_STATE_NONE) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001377 if (cur_session->status == NC_STATUS_RUNNING) {
1378 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001379 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001380
1381 ret = nc_ps_poll_session(cur_session, ts_cur.tv_sec, msg);
1382 switch (ret) {
1383 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1384 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001385 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001386 break;
1387 case NC_PSPOLL_ERROR:
1388 ERR("Session %u: %s.", cur_session->id, msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001389 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001390 break;
1391 case NC_PSPOLL_TIMEOUT:
1392#ifdef NC_ENABLED_SSH
1393 case NC_PSPOLL_SSH_CHANNEL:
1394 case NC_PSPOLL_SSH_MSG:
1395#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001396 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001397 break;
1398 case NC_PSPOLL_RPC:
1399 /* let's keep the state busy, we are not done with this session */
1400 break;
1401 }
1402 } else {
1403 /* session is not fine, let the caller know */
1404 ret = NC_PSPOLL_SESSION_TERM;
1405 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1406 ret |= NC_PSPOLL_SESSION_ERROR;
1407 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001408 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001409 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001410 } else if (cur_ps_session->state == NC_PS_STATE_BUSY) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001411 /* it definitely should not be busy because we have the lock */
1412 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001413 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001414 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001415
1416 /* keep the session locked only in this one case */
1417 if (ret != NC_PSPOLL_RPC) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001418 /* SESSION UNLOCK */
1419 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001420 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001421 } else {
1422 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001423 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001424 }
Michal Vasko428087d2016-01-14 16:04:28 +01001425
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001426 /* something happened */
1427 if (ret != NC_PSPOLL_TIMEOUT) {
1428 break;
1429 }
1430
Michal Vasko9a327362017-01-11 11:31:46 +01001431 if (i == ps->session_count - 1) {
1432 i = 0;
1433 } else {
1434 ++i;
1435 }
1436 } while (i != j);
1437
Michal Vaskoade892d2017-02-22 13:40:35 +01001438 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001439 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001440 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001441 /* update current time */
Michal Vaskoade892d2017-02-22 13:40:35 +01001442 nc_gettimespec(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001443
1444 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1445 /* final timeout */
1446 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001447 }
Michal Vasko428087d2016-01-14 16:04:28 +01001448 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001449 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001450
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001451 /* do we want to return the session? */
1452 switch (ret) {
1453 case NC_PSPOLL_RPC:
1454 case NC_PSPOLL_SESSION_TERM:
1455 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1456#ifdef NC_ENABLED_SSH
1457 case NC_PSPOLL_SSH_CHANNEL:
1458 case NC_PSPOLL_SSH_MSG:
1459#endif
1460 if (session) {
1461 *session = cur_session;
1462 }
1463 ps->last_event_session = i;
1464 break;
1465 default:
1466 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001467 }
Michal Vasko428087d2016-01-14 16:04:28 +01001468
Michal Vaskoade892d2017-02-22 13:40:35 +01001469 /* PS UNLOCK */
1470 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001471
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001472 /* we have some data available and the session is locked */
1473 if (ret == NC_PSPOLL_RPC) {
1474 ret = nc_server_recv_rpc(cur_session, &rpc);
1475 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1476 if (cur_session->status != NC_STATUS_RUNNING) {
1477 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001478 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001479 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001480 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001481 }
1482 } else {
1483 cur_session->opts.server.last_rpc = time(NULL);
1484
1485 /* process RPC, not needed afterwards */
1486 ret |= nc_server_send_reply(cur_session, rpc);
1487 nc_server_rpc_free(rpc, server_opts.ctx);
1488
1489 if (cur_session->status != NC_STATUS_RUNNING) {
1490 ret |= NC_PSPOLL_SESSION_TERM;
1491 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1492 ret |= NC_PSPOLL_SESSION_ERROR;
1493 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001494 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001495 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001496 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001497 }
Michal Vasko428087d2016-01-14 16:04:28 +01001498 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001499
1500 /* SESSION UNLOCK */
1501 nc_session_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001502 }
1503
Michal Vasko48a63ed2016-03-01 09:48:21 +01001504 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001505}
1506
Michal Vaskod09eae62016-02-01 10:32:52 +01001507API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001508nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001509{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001510 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001511 uint16_t i;
1512 struct nc_session *session;
1513
Michal Vasko9a25e932016-02-01 10:36:42 +01001514 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001515 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001516 return;
1517 }
1518
Michal Vasko48a63ed2016-03-01 09:48:21 +01001519 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001520 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001521 return;
1522 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001523
Michal Vasko48a63ed2016-03-01 09:48:21 +01001524 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001525 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001526 nc_session_free(ps->sessions[i]->session, data_free);
1527 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001528 }
1529 free(ps->sessions);
1530 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001531 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001532 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001533 } else {
1534 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001535 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1536 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001537 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001538 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001539 continue;
1540 }
1541
1542 ++i;
1543 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001544 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001545
1546 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001547 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001548}
1549
Radek Krejci53691be2016-02-22 13:58:37 +01001550#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001551
Michal Vaskoe2713da2016-08-22 16:06:40 +02001552API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001553nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001554{
Michal Vasko3031aae2016-01-27 16:07:18 +01001555 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001556 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001557
Michal Vasko45e53ae2016-04-07 11:46:03 +02001558 if (!name) {
1559 ERRARG("name");
1560 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001561 }
1562
Michal Vaskoade892d2017-02-22 13:40:35 +01001563 /* BIND LOCK */
1564 pthread_mutex_lock(&server_opts.bind_lock);
1565
1566 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001567 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001568
1569 /* check name uniqueness */
1570 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001571 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001572 ERR("Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001573 ret = -1;
1574 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001575 }
1576 }
1577
Michal Vasko3031aae2016-01-27 16:07:18 +01001578 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001579 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001580 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001581 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001582 ret = -1;
1583 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001584 }
1585 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001586 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001587
Michal Vaskoe2713da2016-08-22 16:06:40 +02001588 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001589 if (!server_opts.binds) {
1590 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001591 ret = -1;
1592 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001593 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001594
Michal Vasko2e6defd2016-10-07 15:48:15 +02001595 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1596 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1597 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
Michal Vasko0a3f3752016-10-13 14:58:38 +02001598 server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001599
1600 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001601#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001602 case NC_TI_LIBSSH:
1603 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1604 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1605 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001606 ret = -1;
1607 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001608 }
1609 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1610 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1611 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1612 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1613 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001614#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001615#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001616 case NC_TI_OPENSSL:
1617 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1618 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1619 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001620 ret = -1;
1621 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001622 }
1623 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001624#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001625 default:
1626 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01001627 ret = -1;
1628 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001629 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001630
Michal Vaskoade892d2017-02-22 13:40:35 +01001631cleanup:
1632 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001633 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001634
Michal Vaskoade892d2017-02-22 13:40:35 +01001635 /* BIND UNLOCK */
1636 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001637
Michal Vaskoade892d2017-02-22 13:40:35 +01001638 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001639}
1640
Michal Vasko3031aae2016-01-27 16:07:18 +01001641int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001642nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001643{
1644 struct nc_endpt *endpt;
1645 struct nc_bind *bind = NULL;
1646 uint16_t i;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001647 int sock = -1, set_addr, ret = 0;
Michal Vaskoda514772016-02-01 11:32:01 +01001648
Michal Vasko45e53ae2016-04-07 11:46:03 +02001649 if (!endpt_name) {
1650 ERRARG("endpt_name");
1651 return -1;
1652 } else if ((!address && !port) || (address && port)) {
1653 ERRARG("address and port");
1654 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001655 }
1656
Michal Vaskoe2713da2016-08-22 16:06:40 +02001657 if (address) {
1658 set_addr = 1;
1659 } else {
1660 set_addr = 0;
1661 }
1662
Michal Vaskoade892d2017-02-22 13:40:35 +01001663 /* BIND LOCK */
1664 pthread_mutex_lock(&server_opts.bind_lock);
1665
1666 /* ENDPT LOCK */
1667 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001668 if (!endpt) {
Michal Vasko4e455dd2017-03-21 15:33:43 +01001669 /* BIND UNLOCK */
1670 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskoda514772016-02-01 11:32:01 +01001671 return -1;
1672 }
1673
Michal Vaskoe2713da2016-08-22 16:06:40 +02001674 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001675
Michal Vaskoe2713da2016-08-22 16:06:40 +02001676 if (set_addr) {
1677 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001678 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001679 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001680 }
1681
Michal Vaskoe2713da2016-08-22 16:06:40 +02001682 /* we have all the information we need to create a listening socket */
1683 if (address && port) {
1684 /* create new socket, close the old one */
1685 sock = nc_sock_listen(address, port);
1686 if (sock == -1) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001687 ret = -1;
1688 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001689 }
1690
1691 if (bind->sock > -1) {
1692 close(bind->sock);
1693 }
1694 bind->sock = sock;
1695 } /* else we are just setting address or port */
1696
1697 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001698 lydict_remove(server_opts.ctx, bind->address);
1699 bind->address = lydict_insert(server_opts.ctx, address, 0);
1700 } else {
1701 bind->port = port;
1702 }
1703
Michal Vaskoe2713da2016-08-22 16:06:40 +02001704 if (sock > -1) {
1705#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001706 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001707#elif defined(NC_ENABLED_SSH)
1708 VRB("Listening on %s:%u for SSH connections.", address, port);
1709#else
1710 VRB("Listening on %s:%u for TLS connections.", address, port);
1711#endif
1712 }
1713
Michal Vasko4c1fb492017-01-30 14:31:07 +01001714cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001715 /* ENDPT UNLOCK */
1716 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001717
Michal Vaskoade892d2017-02-22 13:40:35 +01001718 /* BIND UNLOCK */
1719 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001720
Michal Vasko4c1fb492017-01-30 14:31:07 +01001721 return ret;
Michal Vaskoda514772016-02-01 11:32:01 +01001722}
1723
Michal Vaskoe2713da2016-08-22 16:06:40 +02001724API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001725nc_server_endpt_set_address(const char *endpt_name, const char *address)
1726{
1727 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1728}
1729
1730API int
1731nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1732{
1733 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1734}
1735
1736API int
Michal Vasko59050372016-11-22 14:33:55 +01001737nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001738{
1739 uint32_t i;
1740 int ret = -1;
1741
Michal Vaskoade892d2017-02-22 13:40:35 +01001742 /* BIND LOCK */
1743 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001744
Michal Vaskoade892d2017-02-22 13:40:35 +01001745 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001746 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001747
Michal Vasko59050372016-11-22 14:33:55 +01001748 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001749 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001750 for (i = 0; i < server_opts.endpt_count; ++i) {
1751 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001752 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001753#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001754 case NC_TI_LIBSSH:
1755 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1756 free(server_opts.endpts[i].opts.ssh);
1757 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001758#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001759#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001760 case NC_TI_OPENSSL:
1761 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1762 free(server_opts.endpts[i].opts.tls);
1763 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001764#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001765 default:
1766 ERRINT;
1767 /* won't get here ...*/
1768 break;
1769 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001770 ret = 0;
1771 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001772 free(server_opts.endpts);
1773 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001774
1775 /* remove all binds */
1776 for (i = 0; i < server_opts.endpt_count; ++i) {
1777 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1778 if (server_opts.binds[i].sock > -1) {
1779 close(server_opts.binds[i].sock);
1780 }
1781 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001782 free(server_opts.binds);
1783 server_opts.binds = NULL;
1784
Michal Vasko3031aae2016-01-27 16:07:18 +01001785 server_opts.endpt_count = 0;
1786
Michal Vasko1a38c862016-01-15 15:50:07 +01001787 } else {
Michal Vasko59050372016-11-22 14:33:55 +01001788 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01001789 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01001790 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001791 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001792 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001793 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001794#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001795 case NC_TI_LIBSSH:
1796 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1797 free(server_opts.endpts[i].opts.ssh);
1798 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001799#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001800#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001801 case NC_TI_OPENSSL:
1802 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1803 free(server_opts.endpts[i].opts.tls);
1804 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001805#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001806 default:
1807 ERRINT;
1808 break;
1809 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001810
Michal Vaskoe2713da2016-08-22 16:06:40 +02001811 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001812 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1813 if (server_opts.binds[i].sock > -1) {
1814 close(server_opts.binds[i].sock);
1815 }
1816
1817 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001818 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001819 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001820 free(server_opts.binds);
1821 server_opts.binds = NULL;
1822 free(server_opts.endpts);
1823 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02001824 } else if (i < server_opts.endpt_count) {
1825 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1826 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01001827 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001828
1829 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01001830 if (name) {
1831 break;
1832 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001833 }
1834 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001835 }
1836
Michal Vaskoade892d2017-02-22 13:40:35 +01001837 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001838 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001839
Michal Vaskoade892d2017-02-22 13:40:35 +01001840 /* BIND UNLOCK */
1841 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001842
1843 return ret;
1844}
1845
Michal Vasko71090fc2016-05-24 16:37:28 +02001846API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001847nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001848{
Michal Vasko71090fc2016-05-24 16:37:28 +02001849 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001850 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001851 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001852 uint16_t port, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001853
Michal Vasko45e53ae2016-04-07 11:46:03 +02001854 if (!server_opts.ctx) {
1855 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001856 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001857 } else if (!session) {
1858 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001859 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001860 }
1861
Michal Vaskoade892d2017-02-22 13:40:35 +01001862 /* BIND LOCK */
1863 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001864
1865 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001866 ERR("No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01001867 /* BIND UNLOCK */
1868 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001869 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001870 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001871
Michal Vaskoe2713da2016-08-22 16:06:40 +02001872 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01001873 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001874 /* BIND UNLOCK */
1875 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001876 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001877 if (!ret) {
1878 return NC_MSG_WOULDBLOCK;
1879 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001880 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001881 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001882
1883 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
1884 /* ENDPT READ LOCK */
1885 pthread_rwlock_rdlock(&server_opts.endpt_lock);
1886
1887 /* BIND UNLOCK */
1888 pthread_mutex_unlock(&server_opts.bind_lock);
1889
Michal Vaskob48aa812016-01-18 14:13:09 +01001890 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001891
Michal Vaskoade892d2017-02-22 13:40:35 +01001892 *session = nc_new_session(0);
Michal Vasko686aa312016-01-21 15:58:18 +01001893 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001894 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001895 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001896 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001897 msgtype = NC_MSG_ERROR;
1898 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001899 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001900 (*session)->status = NC_STATUS_STARTING;
1901 (*session)->side = NC_SERVER;
1902 (*session)->ctx = server_opts.ctx;
1903 (*session)->flags = NC_SESSION_SHAREDCTX;
1904 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1905 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001906
1907 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001908 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001909 pthread_cond_init((*session)->ti_cond, NULL);
1910 *(*session)->ti_inuse = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001911
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001912 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001913#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001914 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1915 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001916 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001917 if (ret < 0) {
1918 msgtype = NC_MSG_ERROR;
1919 goto cleanup;
1920 } else if (!ret) {
1921 msgtype = NC_MSG_WOULDBLOCK;
1922 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001923 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001924 } else
1925#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001926#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001927 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1928 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01001929 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02001930 if (ret < 0) {
1931 msgtype = NC_MSG_ERROR;
1932 goto cleanup;
1933 } else if (!ret) {
1934 msgtype = NC_MSG_WOULDBLOCK;
1935 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001936 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001937 } else
1938#endif
1939 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001940 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001941 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001942 msgtype = NC_MSG_ERROR;
1943 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001944 }
1945
Michal Vasko2cc4c682016-03-01 09:16:48 +01001946 (*session)->data = NULL;
1947
Michal Vaskoade892d2017-02-22 13:40:35 +01001948 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001949 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001950
Michal Vaskob48aa812016-01-18 14:13:09 +01001951 /* assign new SID atomically */
1952 /* LOCK */
1953 pthread_spin_lock(&server_opts.sid_lock);
1954 (*session)->id = server_opts.new_session_id++;
1955 /* UNLOCK */
1956 pthread_spin_unlock(&server_opts.sid_lock);
1957
Michal Vasko9e036d52016-01-08 10:49:26 +01001958 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001959 msgtype = nc_handshake(*session);
1960 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001961 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001962 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001963 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001964 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001965 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001966 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001967
Michal Vasko71090fc2016-05-24 16:37:28 +02001968 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001969
Michal Vasko71090fc2016-05-24 16:37:28 +02001970cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01001971 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001972 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001973
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001974 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001975 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001976 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001977}
1978
Michal Vasko2e6defd2016-10-07 15:48:15 +02001979API int
1980nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1981{
1982 uint16_t i;
1983
1984 if (!name) {
1985 ERRARG("name");
1986 return -1;
1987 } else if (!ti) {
1988 ERRARG("ti");
1989 return -1;
1990 }
1991
1992 /* WRITE LOCK */
1993 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1994
1995 /* check name uniqueness */
1996 for (i = 0; i < server_opts.ch_client_count; ++i) {
1997 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1998 ERR("Call Home client \"%s\" already exists.", name);
1999 /* WRITE UNLOCK */
2000 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2001 return -1;
2002 }
2003 }
2004
2005 ++server_opts.ch_client_count;
2006 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2007 if (!server_opts.ch_clients) {
2008 ERRMEM;
2009 /* WRITE UNLOCK */
2010 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2011 return -1;
2012 }
2013 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
2014 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002015 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
2016 server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002017
2018 switch (ti) {
2019#ifdef NC_ENABLED_SSH
2020 case NC_TI_LIBSSH:
2021 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2022 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
2023 ERRMEM;
2024 /* WRITE UNLOCK */
2025 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2026 return -1;
2027 }
2028 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
2029 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2030 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
2031 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
2032 break;
2033#endif
2034#ifdef NC_ENABLED_TLS
2035 case NC_TI_OPENSSL:
2036 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2037 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
2038 ERRMEM;
2039 /* WRITE UNLOCK */
2040 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2041 return -1;
2042 }
2043 break;
2044#endif
2045 default:
2046 ERRINT;
2047 /* WRITE UNLOCK */
2048 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2049 return -1;
2050 }
2051
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002052 server_opts.ch_clients[server_opts.ch_client_count - 1].conn_type = 0;
2053
Michal Vasko2e6defd2016-10-07 15:48:15 +02002054 /* set CH default options */
2055 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
2056 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
2057
2058 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
2059
2060 /* WRITE UNLOCK */
2061 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2062
2063 return 0;
2064}
2065
2066API int
Michal Vasko59050372016-11-22 14:33:55 +01002067nc_server_ch_del_client(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002068{
2069 uint16_t i, j;
2070 int ret = -1;
2071
2072 /* WRITE LOCK */
2073 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2074
Michal Vasko59050372016-11-22 14:33:55 +01002075 if (!name && !ti) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002076 /* remove all CH clients */
2077 for (i = 0; i < server_opts.ch_client_count; ++i) {
2078 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2079
2080 /* remove all endpoints */
2081 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2082 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2083 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2084 }
2085 free(server_opts.ch_clients[i].ch_endpts);
2086
2087 switch (server_opts.ch_clients[i].ti) {
2088#ifdef NC_ENABLED_SSH
2089 case NC_TI_LIBSSH:
2090 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2091 free(server_opts.ch_clients[i].opts.ssh);
2092 break;
2093#endif
2094#ifdef NC_ENABLED_TLS
2095 case NC_TI_OPENSSL:
2096 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2097 free(server_opts.ch_clients[i].opts.tls);
2098 break;
2099#endif
2100 default:
2101 ERRINT;
2102 /* won't get here ...*/
2103 break;
2104 }
2105
2106 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2107
2108 ret = 0;
2109 }
2110 free(server_opts.ch_clients);
2111 server_opts.ch_clients = NULL;
2112
2113 server_opts.ch_client_count = 0;
2114
2115 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002116 /* remove one client with endpoint(s) or all clients using one protocol */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002117 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002118 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 +02002119 /* remove endpt */
2120 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2121
2122 switch (server_opts.ch_clients[i].ti) {
2123#ifdef NC_ENABLED_SSH
2124 case NC_TI_LIBSSH:
2125 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
2126 free(server_opts.ch_clients[i].opts.ssh);
2127 break;
2128#endif
2129#ifdef NC_ENABLED_TLS
2130 case NC_TI_OPENSSL:
2131 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
2132 free(server_opts.ch_clients[i].opts.tls);
2133 break;
2134#endif
2135 default:
2136 ERRINT;
2137 break;
2138 }
2139
2140 /* remove all endpoints */
2141 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
2142 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
2143 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
2144 }
2145 free(server_opts.ch_clients[i].ch_endpts);
2146
2147 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2148
2149 /* move last client and endpoint(s) to the empty space */
2150 --server_opts.ch_client_count;
2151 if (i < server_opts.ch_client_count) {
2152 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
2153 sizeof *server_opts.ch_clients);
2154 } else if (!server_opts.ch_client_count) {
2155 free(server_opts.ch_clients);
2156 server_opts.ch_clients = NULL;
2157 }
2158
2159 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002160 if (name) {
2161 break;
2162 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002163 }
2164 }
2165 }
2166
2167 /* WRITE UNLOCK */
2168 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2169
2170 return ret;
2171}
2172
2173API int
2174nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
2175{
2176 uint16_t i;
2177 struct nc_ch_client *client;
2178
2179 if (!client_name) {
2180 ERRARG("client_name");
2181 return -1;
2182 } else if (!endpt_name) {
2183 ERRARG("endpt_name");
2184 return -1;
2185 }
2186
2187 /* LOCK */
2188 client = nc_server_ch_client_lock(client_name, 0, NULL);
2189 if (!client) {
2190 return -1;
2191 }
2192
2193 for (i = 0; i < client->ch_endpt_count; ++i) {
2194 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2195 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
2196 /* UNLOCK */
2197 nc_server_ch_client_unlock(client);
2198 return -1;
2199 }
2200 }
2201
2202 ++client->ch_endpt_count;
2203 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2204 if (!client->ch_endpts) {
2205 ERRMEM;
2206 /* UNLOCK */
2207 nc_server_ch_client_unlock(client);
2208 return -1;
2209 }
2210
2211 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
2212 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
2213 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
2214
2215 /* UNLOCK */
2216 nc_server_ch_client_unlock(client);
2217
2218 return 0;
2219}
2220
2221API int
2222nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
2223{
2224 uint16_t i;
2225 int ret = -1;
2226 struct nc_ch_client *client;
2227
2228 if (!client_name) {
2229 ERRARG("client_name");
2230 return -1;
2231 }
2232
2233 /* LOCK */
2234 client = nc_server_ch_client_lock(client_name, 0, NULL);
2235 if (!client) {
2236 return -1;
2237 }
2238
2239 if (!endpt_name) {
2240 /* remove all endpoints */
2241 for (i = 0; i < client->ch_endpt_count; ++i) {
2242 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2243 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2244 }
2245 free(client->ch_endpts);
2246 client->ch_endpts = NULL;
2247 client->ch_endpt_count = 0;
2248
2249 ret = 0;
2250 } else {
2251 for (i = 0; i < client->ch_endpt_count; ++i) {
2252 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2253 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2254 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002255
Michal Vasko4f921012016-10-20 14:07:45 +02002256 /* move last endpoint to the empty space */
2257 --client->ch_endpt_count;
2258 if (i < client->ch_endpt_count) {
2259 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2260 } else if (!server_opts.ch_client_count) {
2261 free(server_opts.ch_clients);
2262 server_opts.ch_clients = NULL;
2263 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002264
Michal Vasko4f921012016-10-20 14:07:45 +02002265 ret = 0;
2266 break;
2267 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002268 }
2269 }
2270
2271 /* UNLOCK */
2272 nc_server_ch_client_unlock(client);
2273
2274 return ret;
2275}
2276
2277API int
2278nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2279{
2280 uint16_t i;
2281 int ret = -1;
2282 struct nc_ch_client *client;
2283
2284 if (!client_name) {
2285 ERRARG("client_name");
2286 return -1;
2287 } else if (!endpt_name) {
2288 ERRARG("endpt_name");
2289 return -1;
2290 } else if (!address) {
2291 ERRARG("address");
2292 return -1;
2293 }
2294
2295 /* LOCK */
2296 client = nc_server_ch_client_lock(client_name, 0, NULL);
2297 if (!client) {
2298 return -1;
2299 }
2300
2301 for (i = 0; i < client->ch_endpt_count; ++i) {
2302 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2303 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2304 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2305
2306 ret = 0;
2307 break;
2308 }
2309 }
2310
2311 /* UNLOCK */
2312 nc_server_ch_client_unlock(client);
2313
2314 if (ret == -1) {
2315 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2316 }
2317
2318 return ret;
2319}
2320
2321API int
2322nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2323{
2324 uint16_t i;
2325 int ret = -1;
2326 struct nc_ch_client *client;
2327
2328 if (!client_name) {
2329 ERRARG("client_name");
2330 return -1;
2331 } else if (!endpt_name) {
2332 ERRARG("endpt_name");
2333 return -1;
2334 } else if (!port) {
2335 ERRARG("port");
2336 return -1;
2337 }
2338
2339 /* LOCK */
2340 client = nc_server_ch_client_lock(client_name, 0, NULL);
2341 if (!client) {
2342 return -1;
2343 }
2344
2345 for (i = 0; i < client->ch_endpt_count; ++i) {
2346 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2347 client->ch_endpts[i].port = port;
2348
2349 ret = 0;
2350 break;
2351 }
2352 }
2353
2354 /* UNLOCK */
2355 nc_server_ch_client_unlock(client);
2356
2357 if (ret == -1) {
2358 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2359 }
2360
2361 return ret;
2362}
2363
2364API int
2365nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2366{
2367 struct nc_ch_client *client;
2368
2369 if (!client_name) {
2370 ERRARG("client_name");
2371 return -1;
2372 } else if (!conn_type) {
2373 ERRARG("conn_type");
2374 return -1;
2375 }
2376
2377 /* LOCK */
2378 client = nc_server_ch_client_lock(client_name, 0, NULL);
2379 if (!client) {
2380 return -1;
2381 }
2382
2383 if (client->conn_type != conn_type) {
2384 client->conn_type = conn_type;
2385
2386 /* set default options */
2387 switch (conn_type) {
2388 case NC_CH_PERSIST:
2389 client->conn.persist.idle_timeout = 86400;
2390 client->conn.persist.ka_max_wait = 30;
2391 client->conn.persist.ka_max_attempts = 3;
2392 break;
2393 case NC_CH_PERIOD:
2394 client->conn.period.idle_timeout = 300;
2395 client->conn.period.reconnect_timeout = 60;
2396 break;
2397 default:
2398 ERRINT;
2399 break;
2400 }
2401 }
2402
2403 /* UNLOCK */
2404 nc_server_ch_client_unlock(client);
2405
2406 return 0;
2407}
2408
2409API int
2410nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2411{
2412 struct nc_ch_client *client;
2413
2414 if (!client_name) {
2415 ERRARG("client_name");
2416 return -1;
2417 }
2418
2419 /* LOCK */
2420 client = nc_server_ch_client_lock(client_name, 0, NULL);
2421 if (!client) {
2422 return -1;
2423 }
2424
2425 if (client->conn_type != NC_CH_PERSIST) {
2426 ERR("Call Home client \"%s\" is not of persistent connection type.");
2427 /* UNLOCK */
2428 nc_server_ch_client_unlock(client);
2429 return -1;
2430 }
2431
2432 client->conn.persist.idle_timeout = idle_timeout;
2433
2434 /* UNLOCK */
2435 nc_server_ch_client_unlock(client);
2436
2437 return 0;
2438}
2439
2440API int
2441nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2442{
2443 struct nc_ch_client *client;
2444
2445 if (!client_name) {
2446 ERRARG("client_name");
2447 return -1;
2448 } else if (!max_wait) {
2449 ERRARG("max_wait");
2450 return -1;
2451 }
2452
2453 /* LOCK */
2454 client = nc_server_ch_client_lock(client_name, 0, NULL);
2455 if (!client) {
2456 return -1;
2457 }
2458
2459 if (client->conn_type != NC_CH_PERSIST) {
2460 ERR("Call Home client \"%s\" is not of persistent connection type.");
2461 /* UNLOCK */
2462 nc_server_ch_client_unlock(client);
2463 return -1;
2464 }
2465
2466 client->conn.persist.ka_max_wait = max_wait;
2467
2468 /* UNLOCK */
2469 nc_server_ch_client_unlock(client);
2470
2471 return 0;
2472}
2473
2474API int
2475nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2476{
2477 struct nc_ch_client *client;
2478
2479 if (!client_name) {
2480 ERRARG("client_name");
2481 return -1;
2482 }
2483
2484 /* LOCK */
2485 client = nc_server_ch_client_lock(client_name, 0, NULL);
2486 if (!client) {
2487 return -1;
2488 }
2489
2490 if (client->conn_type != NC_CH_PERSIST) {
2491 ERR("Call Home client \"%s\" is not of persistent connection type.");
2492 /* UNLOCK */
2493 nc_server_ch_client_unlock(client);
2494 return -1;
2495 }
2496
2497 client->conn.persist.ka_max_attempts = max_attempts;
2498
2499 /* UNLOCK */
2500 nc_server_ch_client_unlock(client);
2501
2502 return 0;
2503}
2504
2505API int
2506nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2507{
2508 struct nc_ch_client *client;
2509
2510 if (!client_name) {
2511 ERRARG("client_name");
2512 return -1;
2513 }
2514
2515 /* LOCK */
2516 client = nc_server_ch_client_lock(client_name, 0, NULL);
2517 if (!client) {
2518 return -1;
2519 }
2520
2521 if (client->conn_type != NC_CH_PERIOD) {
2522 ERR("Call Home client \"%s\" is not of periodic connection type.");
2523 /* UNLOCK */
2524 nc_server_ch_client_unlock(client);
2525 return -1;
2526 }
2527
2528 client->conn.period.idle_timeout = idle_timeout;
2529
2530 /* UNLOCK */
2531 nc_server_ch_client_unlock(client);
2532
2533 return 0;
2534}
2535
2536API int
2537nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2538{
2539 struct nc_ch_client *client;
2540
2541 if (!client_name) {
2542 ERRARG("client_name");
2543 return -1;
2544 } else if (!reconnect_timeout) {
2545 ERRARG("reconnect_timeout");
2546 return -1;
2547 }
2548
2549 /* LOCK */
2550 client = nc_server_ch_client_lock(client_name, 0, NULL);
2551 if (!client) {
2552 return -1;
2553 }
2554
2555 if (client->conn_type != NC_CH_PERIOD) {
2556 ERR("Call Home client \"%s\" is not of periodic connection type.");
2557 /* UNLOCK */
2558 nc_server_ch_client_unlock(client);
2559 return -1;
2560 }
2561
2562 client->conn.period.reconnect_timeout = reconnect_timeout;
2563
2564 /* UNLOCK */
2565 nc_server_ch_client_unlock(client);
2566
2567 return 0;
2568}
2569
2570API int
2571nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2572{
2573 struct nc_ch_client *client;
2574
2575 if (!client_name) {
2576 ERRARG("client_name");
2577 return -1;
2578 }
2579
2580 /* LOCK */
2581 client = nc_server_ch_client_lock(client_name, 0, NULL);
2582 if (!client) {
2583 return -1;
2584 }
2585
2586 client->start_with = start_with;
2587
2588 /* UNLOCK */
2589 nc_server_ch_client_unlock(client);
2590
2591 return 0;
2592}
2593
2594API int
2595nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2596{
2597 struct nc_ch_client *client;
2598
2599 if (!client_name) {
2600 ERRARG("client_name");
2601 return -1;
2602 } else if (!max_attempts) {
2603 ERRARG("max_attempts");
2604 return -1;
2605 }
2606
2607 /* LOCK */
2608 client = nc_server_ch_client_lock(client_name, 0, NULL);
2609 if (!client) {
2610 return -1;
2611 }
2612
2613 client->max_attempts = max_attempts;
2614
2615 /* UNLOCK */
2616 nc_server_ch_client_unlock(client);
2617
2618 return 0;
2619}
2620
2621/* client lock is expected to be held */
2622static NC_MSG_TYPE
2623nc_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 +01002624{
Michal Vasko71090fc2016-05-24 16:37:28 +02002625 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002626 int sock, ret;
2627
Michal Vasko2e6defd2016-10-07 15:48:15 +02002628 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002629 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002630 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002631 }
2632
Michal Vaskoade892d2017-02-22 13:40:35 +01002633 *session = nc_new_session(0);
Michal Vaskob05053d2016-01-22 16:12:06 +01002634 if (!(*session)) {
2635 ERRMEM;
2636 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002637 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002638 }
2639 (*session)->status = NC_STATUS_STARTING;
2640 (*session)->side = NC_SERVER;
2641 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002642 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002643 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2644 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002645
2646 /* transport lock */
Michal Vaskob05053d2016-01-22 16:12:06 +01002647 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01002648 pthread_cond_init((*session)->ti_cond, NULL);
2649 *(*session)->ti_inuse = 0;
Michal Vaskob05053d2016-01-22 16:12:06 +01002650
2651 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002652#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002653 if (client->ti == NC_TI_LIBSSH) {
2654 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002655 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002656 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002657
Michal Vasko71090fc2016-05-24 16:37:28 +02002658 if (ret < 0) {
2659 msgtype = NC_MSG_ERROR;
2660 goto fail;
2661 } else if (!ret) {
2662 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002663 goto fail;
2664 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002665 } else
2666#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002667#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002668 if (client->ti == NC_TI_OPENSSL) {
2669 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002670 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002671 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002672
Michal Vasko71090fc2016-05-24 16:37:28 +02002673 if (ret < 0) {
2674 msgtype = NC_MSG_ERROR;
2675 goto fail;
2676 } else if (!ret) {
2677 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002678 goto fail;
2679 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002680 } else
2681#endif
2682 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002683 ERRINT;
2684 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002685 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002686 goto fail;
2687 }
2688
2689 /* assign new SID atomically */
2690 /* LOCK */
2691 pthread_spin_lock(&server_opts.sid_lock);
2692 (*session)->id = server_opts.new_session_id++;
2693 /* UNLOCK */
2694 pthread_spin_unlock(&server_opts.sid_lock);
2695
2696 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002697 msgtype = nc_handshake(*session);
2698 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002699 goto fail;
2700 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002701 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002702 (*session)->status = NC_STATUS_RUNNING;
2703
Michal Vasko71090fc2016-05-24 16:37:28 +02002704 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002705
2706fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002707 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002708 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002709 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002710}
2711
Michal Vasko2e6defd2016-10-07 15:48:15 +02002712struct nc_ch_client_thread_arg {
2713 char *client_name;
2714 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2715};
2716
2717static struct nc_ch_client *
2718nc_server_ch_client_with_endpt_lock(const char *name)
2719{
2720 struct nc_ch_client *client;
2721
2722 while (1) {
2723 /* LOCK */
2724 client = nc_server_ch_client_lock(name, 0, NULL);
2725 if (!client) {
2726 return NULL;
2727 }
2728 if (client->ch_endpt_count) {
2729 return client;
2730 }
2731 /* no endpoints defined yet */
2732
2733 /* UNLOCK */
2734 nc_server_ch_client_unlock(client);
2735
2736 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2737 }
2738
2739 return NULL;
2740}
2741
2742static int
2743nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2744{
2745 int ret;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002746 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002747 struct timespec ts;
2748 struct nc_ch_client *client;
2749
2750 /* session created, initialize condition */
2751 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2752 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2753 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2754 ERRMEM;
2755 nc_session_free(session, NULL);
2756 return -1;
2757 }
2758 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2759 pthread_cond_init(session->opts.server.ch_cond, NULL);
2760
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002761 session->flags |= NC_SESSION_CALLHOME;
2762
Michal Vasko2e6defd2016-10-07 15:48:15 +02002763 /* CH LOCK */
2764 pthread_mutex_lock(session->opts.server.ch_lock);
2765
2766 /* give the session to the user */
2767 data->session_clb(data->client_name, session);
2768
2769 do {
2770 nc_gettimespec(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01002771 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002772
2773 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2774 if (ret && (ret != ETIMEDOUT)) {
2775 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2776 goto ch_client_remove;
2777 }
2778
2779 /* check whether the client was not removed */
2780 /* LOCK */
2781 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2782 if (!client) {
2783 /* client was removed, finish thread */
2784 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2785 data->client_name);
2786 goto ch_client_remove;
2787 }
2788
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002789 if (client->conn_type == NC_CH_PERSIST) {
2790 /* TODO keep-alives */
2791 idle_timeout = client->conn.persist.idle_timeout;
2792 } else {
2793 idle_timeout = client->conn.period.idle_timeout;
2794 }
2795
Michal Vasko3486a7c2017-03-03 13:28:07 +01002796 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 +02002797 VRB("Call Home client \"%s\" session %u: session idle timeout elapsed.", client->name, session->id);
2798 session->status = NC_STATUS_INVALID;
2799 session->term_reason = NC_SESSION_TERM_TIMEOUT;
2800 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002801
2802 /* UNLOCK */
2803 nc_server_ch_client_unlock(client);
2804
2805 } while (session->status == NC_STATUS_RUNNING);
2806
2807 /* CH UNLOCK */
2808 pthread_mutex_unlock(session->opts.server.ch_lock);
2809
2810 return 0;
2811
2812ch_client_remove:
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002813 /* make the session a standard one */
2814 pthread_cond_destroy(session->opts.server.ch_cond);
2815 free(session->opts.server.ch_cond);
2816 session->opts.server.ch_cond = NULL;
2817
2818 session->flags &= ~NC_SESSION_CALLHOME;
2819
Michal Vasko2e6defd2016-10-07 15:48:15 +02002820 /* CH UNLOCK */
2821 pthread_mutex_unlock(session->opts.server.ch_lock);
2822
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002823 pthread_mutex_destroy(session->opts.server.ch_lock);
2824 free(session->opts.server.ch_lock);
2825 session->opts.server.ch_lock = NULL;
2826
Michal Vasko2e6defd2016-10-07 15:48:15 +02002827 return 1;
2828}
2829
2830static void *
2831nc_ch_client_thread(void *arg)
2832{
2833 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2834 NC_MSG_TYPE msgtype;
2835 uint8_t cur_attempts = 0;
2836 uint16_t i;
Michal Vasko9550cf12017-03-21 15:33:58 +01002837 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002838 struct nc_ch_endpt *cur_endpt;
2839 struct nc_session *session;
2840 struct nc_ch_client *client;
2841
2842 /* LOCK */
2843 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2844 if (!client) {
2845 goto cleanup;
2846 }
2847
2848 cur_endpt = &client->ch_endpts[0];
2849 cur_endpt_name = strdup(cur_endpt->name);
2850
Michal Vasko29af44b2016-10-13 10:59:55 +02002851 VRB("Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002852 while (1) {
2853 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2854
2855 if (msgtype == NC_MSG_HELLO) {
2856 /* UNLOCK */
2857 nc_server_ch_client_unlock(client);
2858
Michal Vasko29af44b2016-10-13 10:59:55 +02002859 VRB("Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002860 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2861 goto cleanup;
2862 }
Michal Vasko29af44b2016-10-13 10:59:55 +02002863 VRB("Call Home client \"%s\" session terminated, reconnecting...", client->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002864
2865 /* LOCK */
2866 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2867 if (!client) {
2868 goto cleanup;
2869 }
2870
2871 /* session changed status -> it was disconnected for whatever reason,
2872 * persistent connection immediately tries to reconnect, periodic waits some first */
2873 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002874 /* UNLOCK */
2875 nc_server_ch_client_unlock(client);
2876
2877 /* TODO wake up sometimes to check for new notifications */
2878 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2879
2880 /* LOCK */
2881 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2882 if (!client) {
2883 goto cleanup;
2884 }
2885 }
2886
2887 /* set next endpoint to try */
2888 if (client->start_with == NC_CH_FIRST_LISTED) {
2889 cur_endpt = &client->ch_endpts[0];
2890 free(cur_endpt_name);
2891 cur_endpt_name = strdup(cur_endpt->name);
2892 } /* else we keep the current one */
2893 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02002894 /* UNLOCK */
2895 nc_server_ch_client_unlock(client);
2896
Michal Vasko2e6defd2016-10-07 15:48:15 +02002897 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002898 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
2899
Michal Vasko6bb116b2016-10-26 13:53:46 +02002900 /* LOCK */
2901 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2902 if (!client) {
2903 goto cleanup;
2904 }
2905
Michal Vasko2e6defd2016-10-07 15:48:15 +02002906 ++cur_attempts;
2907 if (cur_attempts == client->max_attempts) {
2908 for (i = 0; i < client->ch_endpt_count; ++i) {
2909 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2910 break;
2911 }
2912 }
2913 if (i < client->ch_endpt_count - 1) {
2914 /* just go to the next endpoint */
2915 cur_endpt = &client->ch_endpts[i + 1];
2916 free(cur_endpt_name);
2917 cur_endpt_name = strdup(cur_endpt->name);
2918 } else {
2919 /* cur_endpoint was removed or is the last, either way start with the first one */
2920 cur_endpt = &client->ch_endpts[0];
2921 free(cur_endpt_name);
2922 cur_endpt_name = strdup(cur_endpt->name);
2923 }
2924
2925 cur_attempts = 0;
2926 } /* else we keep the current one */
2927 }
2928 }
2929
2930cleanup:
2931 VRB("Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01002932 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002933 free(data->client_name);
2934 free(data);
2935 return NULL;
2936}
2937
2938API int
2939nc_connect_ch_client_dispatch(const char *client_name,
2940 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2941 int ret;
2942 pthread_t tid;
2943 struct nc_ch_client_thread_arg *arg;
2944
2945 if (!client_name) {
2946 ERRARG("client_name");
2947 return -1;
2948 } else if (!session_clb) {
2949 ERRARG("session_clb");
2950 return -1;
2951 }
2952
2953 arg = malloc(sizeof *arg);
2954 if (!arg) {
2955 ERRMEM;
2956 return -1;
2957 }
2958 arg->client_name = strdup(client_name);
2959 if (!arg->client_name) {
2960 ERRMEM;
2961 free(arg);
2962 return -1;
2963 }
2964 arg->session_clb = session_clb;
2965
2966 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2967 if (ret) {
2968 ERR("Creating a new thread failed (%s).", strerror(ret));
2969 free(arg->client_name);
2970 free(arg);
2971 return -1;
2972 }
2973 /* the thread now manages arg */
2974
2975 pthread_detach(tid);
2976
2977 return 0;
2978}
2979
Radek Krejci53691be2016-02-22 13:58:37 +01002980#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002981
Michal Vaskoe8e07702017-03-15 10:19:30 +01002982API int
2983nc_server_endpt_count(void)
2984{
2985 return server_opts.endpt_count;
2986}
2987
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002988API time_t
2989nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02002990{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002991 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02002992 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002993 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02002994 }
2995
Michal Vasko2e6defd2016-10-07 15:48:15 +02002996 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02002997}
Michal Vasko3486a7c2017-03-03 13:28:07 +01002998
2999API void
3000nc_session_set_notif_status(struct nc_session *session, int notif_status)
3001{
3002 if (!session || (session->side != NC_SERVER)) {
3003 ERRARG("session");
3004 return;
3005 }
3006
3007 session->opts.server.ntf_status = (notif_status ? 1 : 0);
3008}
3009
3010API int
3011nc_session_get_notif_status(const struct nc_session *session)
3012{
3013 if (!session || (session->side != NC_SERVER)) {
3014 ERRARG("session");
3015 return 0;
3016 }
3017
3018 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003019}